config: allow env.yaml to contain configuration in a single file
- add `SingleFileScope` to configuration, which allows us to pull config sections from a single file. - update `env.yaml` and tests to ensure that the env.yaml schema works when pulling configurtion from the env file.
This commit is contained in:
parent
9ee2623486
commit
6af5dfbbc2
@ -146,6 +146,26 @@ def has_method(cls, name):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def union_dicts(*dicts):
|
||||||
|
"""Use update() to combine all dicts into one.
|
||||||
|
|
||||||
|
This builds a new dictionary, into which we ``update()`` each element
|
||||||
|
of ``dicts`` in order. Items from later dictionaries will override
|
||||||
|
items from earlier dictionaries.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
dicts (list): list of dictionaries
|
||||||
|
|
||||||
|
Return: (dict): a merged dictionary containing combined keys and
|
||||||
|
values from ``dicts``.
|
||||||
|
|
||||||
|
"""
|
||||||
|
result = {}
|
||||||
|
for d in dicts:
|
||||||
|
result.update(d)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
class memoized(object):
|
class memoized(object):
|
||||||
"""Decorator that caches the results of a function, storing them
|
"""Decorator that caches the results of a function, storing them
|
||||||
in an attribute of that function."""
|
in an attribute of that function."""
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from six import StringIO
|
from six import StringIO, text_type
|
||||||
|
|
||||||
from llnl.util.tty import terminal_size
|
from llnl.util.tty import terminal_size
|
||||||
from llnl.util.tty.color import clen, cextra
|
from llnl.util.tty.color import clen, cextra
|
||||||
@ -137,7 +137,7 @@ def colify(elts, **options):
|
|||||||
% next(options.iterkeys()))
|
% next(options.iterkeys()))
|
||||||
|
|
||||||
# elts needs to be an array of strings so we can count the elements
|
# elts needs to be an array of strings so we can count the elements
|
||||||
elts = [str(elt) for elt in elts]
|
elts = [text_type(elt) for elt in elts]
|
||||||
if not elts:
|
if not elts:
|
||||||
return (0, ())
|
return (0, ())
|
||||||
|
|
||||||
|
@ -44,17 +44,16 @@
|
|||||||
# =============== Modifies Environment
|
# =============== Modifies Environment
|
||||||
|
|
||||||
def setup_create_parser(subparser):
|
def setup_create_parser(subparser):
|
||||||
"""create a new environment"""
|
"""create a new environment."""
|
||||||
|
subparser.add_argument('env', help='name of environment to create')
|
||||||
subparser.add_argument(
|
subparser.add_argument(
|
||||||
'--init-file', dest='init_file',
|
'envfile', nargs='?', help='optional initialization file')
|
||||||
help='File with user specs to add and configuration yaml to use')
|
|
||||||
|
|
||||||
|
|
||||||
def environment_create(args):
|
def environment_create(args):
|
||||||
if os.path.exists(ev.root(args.environment)):
|
if os.path.exists(ev.root(args.env)):
|
||||||
raise tty.die("Environment already exists: " + args.environment)
|
raise tty.die("Environment already exists: " + args.env)
|
||||||
|
_environment_create(args.env)
|
||||||
_environment_create(args.environment)
|
|
||||||
|
|
||||||
|
|
||||||
def _environment_create(name, init_config=None):
|
def _environment_create(name, init_config=None):
|
||||||
@ -310,7 +309,6 @@ def add_use_repo_argument(cmd_parser):
|
|||||||
|
|
||||||
|
|
||||||
def setup_parser(subparser):
|
def setup_parser(subparser):
|
||||||
subparser.add_argument('environment', help="name of environment")
|
|
||||||
sp = subparser.add_subparsers(
|
sp = subparser.add_subparsers(
|
||||||
metavar='SUBCOMMAND', dest='environment_command')
|
metavar='SUBCOMMAND', dest='environment_command')
|
||||||
|
|
||||||
@ -324,6 +322,7 @@ def setup_parser(subparser):
|
|||||||
|
|
||||||
def env(parser, args, **kwargs):
|
def env(parser, args, **kwargs):
|
||||||
"""Look for a function called environment_<name> and call it."""
|
"""Look for a function called environment_<name> and call it."""
|
||||||
|
|
||||||
function_name = 'environment_%s' % args.environment_command
|
function_name = 'environment_%s' % args.environment_command
|
||||||
action = globals()[function_name]
|
action = globals()[function_name]
|
||||||
action(args)
|
action(args)
|
||||||
|
@ -177,6 +177,8 @@ def get_section(self, section):
|
|||||||
def write_section(self, section):
|
def write_section(self, section):
|
||||||
filename = self.get_section_filename(section)
|
filename = self.get_section_filename(section)
|
||||||
data = self.get_section(section)
|
data = self.get_section(section)
|
||||||
|
_validate(data, section_schemas[section])
|
||||||
|
|
||||||
try:
|
try:
|
||||||
mkdirp(self.path)
|
mkdirp(self.path)
|
||||||
with open(filename, 'w') as f:
|
with open(filename, 'w') as f:
|
||||||
@ -194,6 +196,77 @@ def __repr__(self):
|
|||||||
return '<ConfigScope: %s: %s>' % (self.name, self.path)
|
return '<ConfigScope: %s: %s>' % (self.name, self.path)
|
||||||
|
|
||||||
|
|
||||||
|
class SingleFileScope(ConfigScope):
|
||||||
|
"""This class represents a configuration scope in a single YAML file."""
|
||||||
|
def __init__(self, name, path, schema, yaml_path=None):
|
||||||
|
"""Similar to ``ConfigScope`` but can be embedded in another schema.
|
||||||
|
|
||||||
|
Arguments:
|
||||||
|
schema (dict): jsonschema for the file to read
|
||||||
|
yaml_path (list): list of dict keys in the schema where
|
||||||
|
config data can be found.
|
||||||
|
|
||||||
|
"""
|
||||||
|
super(SingleFileScope, self).__init__(name, path)
|
||||||
|
self._raw_data = None
|
||||||
|
self.schema = schema
|
||||||
|
self.yaml_path = yaml_path or []
|
||||||
|
|
||||||
|
def get_section_filename(self, section):
|
||||||
|
return self.path
|
||||||
|
|
||||||
|
def get_section(self, section):
|
||||||
|
# read raw data from the file, which looks like:
|
||||||
|
# {
|
||||||
|
# 'config': {
|
||||||
|
# ... data ...
|
||||||
|
# },
|
||||||
|
# 'packages': {
|
||||||
|
# ... data ...
|
||||||
|
# },
|
||||||
|
# }
|
||||||
|
if self._raw_data is None:
|
||||||
|
self._raw_data = _read_config_file(self.path, self.schema)
|
||||||
|
if self._raw_data is None:
|
||||||
|
return None
|
||||||
|
|
||||||
|
for key in self.yaml_path:
|
||||||
|
self._raw_data = self._raw_data[key]
|
||||||
|
|
||||||
|
# data in self.sections looks (awkwardly) like this:
|
||||||
|
# {
|
||||||
|
# 'config': {
|
||||||
|
# 'config': {
|
||||||
|
# ... data ...
|
||||||
|
# }
|
||||||
|
# },
|
||||||
|
# 'packages': {
|
||||||
|
# 'packages': {
|
||||||
|
# ... data ...
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
# }
|
||||||
|
return self.sections.setdefault(
|
||||||
|
section, {section: self._raw_data.get(section)})
|
||||||
|
|
||||||
|
def write_section(self, section):
|
||||||
|
_validate(self.sections, self.schema)
|
||||||
|
try:
|
||||||
|
parent = os.path.dirname(self.path)
|
||||||
|
mkdirp(parent)
|
||||||
|
|
||||||
|
tmp = os.path.join(parent, '.%s.tmp' % self.path)
|
||||||
|
with open(tmp, 'w') as f:
|
||||||
|
syaml.dump(self.sections, stream=f, default_flow_style=False)
|
||||||
|
os.path.move(tmp, self.path)
|
||||||
|
except (yaml.YAMLError, IOError) as e:
|
||||||
|
raise ConfigFileError(
|
||||||
|
"Error writing to config file: '%s'" % str(e))
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return '<SingleFileScope: %s: %s>' % (self.name, self.path)
|
||||||
|
|
||||||
|
|
||||||
class ImmutableConfigScope(ConfigScope):
|
class ImmutableConfigScope(ConfigScope):
|
||||||
"""A configuration scope that cannot be written to.
|
"""A configuration scope that cannot be written to.
|
||||||
|
|
||||||
@ -215,7 +288,7 @@ class InternalConfigScope(ConfigScope):
|
|||||||
override settings from files.
|
override settings from files.
|
||||||
"""
|
"""
|
||||||
def __init__(self, name, data=None):
|
def __init__(self, name, data=None):
|
||||||
self.name = name
|
super(InternalConfigScope, self).__init__(name, None)
|
||||||
self.sections = syaml.syaml_dict()
|
self.sections = syaml.syaml_dict()
|
||||||
|
|
||||||
if data:
|
if data:
|
||||||
|
@ -1,27 +1,8 @@
|
|||||||
##############################################################################
|
# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
|
||||||
# Copyright (c) 2013-2018, Lawrence Livermore National Security, LLC.
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
# Produced at the Lawrence Livermore National Laboratory.
|
|
||||||
#
|
#
|
||||||
# This file is part of Spack.
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
# 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
|
|
||||||
##############################################################################
|
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
import shutil
|
import shutil
|
||||||
|
@ -6,41 +6,52 @@
|
|||||||
"""Schema for env.yaml configuration file.
|
"""Schema for env.yaml configuration file.
|
||||||
|
|
||||||
.. literalinclude:: ../spack/schema/env.py
|
.. literalinclude:: ../spack/schema/env.py
|
||||||
:lines: 32-
|
:lines: 36-
|
||||||
"""
|
"""
|
||||||
|
from llnl.util.lang import union_dicts
|
||||||
|
|
||||||
|
import spack.schema.merged
|
||||||
|
import spack.schema.modules
|
||||||
|
|
||||||
|
|
||||||
schema = {
|
schema = {
|
||||||
'$schema': 'http://json-schema.org/schema#',
|
'$schema': 'http://json-schema.org/schema#',
|
||||||
'title': 'Spack environment file schema',
|
'title': 'Spack environment file schema',
|
||||||
|
'definitions': spack.schema.modules.definitions,
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'additionalProperties': False,
|
'additionalProperties': False,
|
||||||
'patternProperties': {
|
'patternProperties': {
|
||||||
'^env|spack$': {
|
'^env|spack$': {
|
||||||
'type': 'object',
|
'type': 'object',
|
||||||
'default': {},
|
'default': {},
|
||||||
'properties': {
|
'additionalProperties': False,
|
||||||
|
'properties': union_dicts(
|
||||||
|
# merged configuration scope schemas
|
||||||
|
spack.schema.merged.properties,
|
||||||
|
# extra environment schema properties
|
||||||
|
{
|
||||||
'include': {
|
'include': {
|
||||||
'type': 'array',
|
'type': 'array',
|
||||||
'items': {
|
'items': {
|
||||||
'type': 'string'
|
'type': 'string'
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
'specs': {
|
'specs': {
|
||||||
'type': 'object',
|
# Specs is a list of specs, which can have
|
||||||
'default': {},
|
# optional additional properties in a sub-dict
|
||||||
'additionalProperties': False,
|
'type': 'array',
|
||||||
'patternProperties': {
|
'default': [],
|
||||||
r'\w[\w-]*': { # user spec
|
|
||||||
'type': 'object',
|
|
||||||
'default': {},
|
|
||||||
'additionalProperties': False,
|
'additionalProperties': False,
|
||||||
|
'items': {
|
||||||
|
'anyOf': [
|
||||||
|
{'type': 'string'},
|
||||||
|
{'type': 'null'},
|
||||||
|
{'type': 'object'},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
40
lib/spack/spack/schema/merged.py
Normal file
40
lib/spack/spack/schema/merged.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
|
||||||
|
# Spack Project Developers. See the top-level COPYRIGHT file for details.
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
|
||||||
|
|
||||||
|
"""Schema for configuration merged into one file.
|
||||||
|
|
||||||
|
.. literalinclude:: ../spack/schema/merged.py
|
||||||
|
:lines: 40-
|
||||||
|
"""
|
||||||
|
from llnl.util.lang import union_dicts
|
||||||
|
|
||||||
|
import spack.schema.compilers
|
||||||
|
import spack.schema.config
|
||||||
|
import spack.schema.mirrors
|
||||||
|
import spack.schema.modules
|
||||||
|
import spack.schema.packages
|
||||||
|
import spack.schema.repos
|
||||||
|
|
||||||
|
|
||||||
|
#: Properties for inclusion in other schemas
|
||||||
|
properties = union_dicts(
|
||||||
|
spack.schema.compilers.properties,
|
||||||
|
spack.schema.config.properties,
|
||||||
|
spack.schema.mirrors.properties,
|
||||||
|
spack.schema.modules.properties,
|
||||||
|
spack.schema.packages.properties,
|
||||||
|
spack.schema.repos.properties
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
#: Full schema with metadata
|
||||||
|
schema = {
|
||||||
|
'$schema': 'http://json-schema.org/schema#',
|
||||||
|
'title': 'Spack merged configuration file schema',
|
||||||
|
'definitions': spack.schema.modules.definitions,
|
||||||
|
'type': 'object',
|
||||||
|
'additionalProperties': False,
|
||||||
|
'properties': properties,
|
||||||
|
}
|
@ -18,6 +18,7 @@
|
|||||||
import spack.config
|
import spack.config
|
||||||
import spack.schema.compilers
|
import spack.schema.compilers
|
||||||
import spack.schema.config
|
import spack.schema.config
|
||||||
|
import spack.schema.env
|
||||||
import spack.schema.packages
|
import spack.schema.packages
|
||||||
import spack.schema.mirrors
|
import spack.schema.mirrors
|
||||||
import spack.schema.repos
|
import spack.schema.repos
|
||||||
@ -49,7 +50,7 @@
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture()
|
@pytest.fixture()
|
||||||
def config(tmpdir):
|
def mock_config(tmpdir):
|
||||||
"""Mocks the configuration scope."""
|
"""Mocks the configuration scope."""
|
||||||
real_configuration = spack.config.config
|
real_configuration = spack.config.config
|
||||||
|
|
||||||
@ -216,7 +217,7 @@ def compiler_specs():
|
|||||||
return CompilerSpecs(a=a, b=b)
|
return CompilerSpecs(a=a, b=b)
|
||||||
|
|
||||||
|
|
||||||
def test_write_key_in_memory(config, compiler_specs):
|
def test_write_key_in_memory(mock_config, compiler_specs):
|
||||||
# Write b_comps "on top of" a_comps.
|
# Write b_comps "on top of" a_comps.
|
||||||
spack.config.set('compilers', a_comps['compilers'], scope='low')
|
spack.config.set('compilers', a_comps['compilers'], scope='low')
|
||||||
spack.config.set('compilers', b_comps['compilers'], scope='high')
|
spack.config.set('compilers', b_comps['compilers'], scope='high')
|
||||||
@ -226,7 +227,7 @@ def test_write_key_in_memory(config, compiler_specs):
|
|||||||
check_compiler_config(b_comps['compilers'], *compiler_specs.b)
|
check_compiler_config(b_comps['compilers'], *compiler_specs.b)
|
||||||
|
|
||||||
|
|
||||||
def test_write_key_to_disk(config, compiler_specs):
|
def test_write_key_to_disk(mock_config, compiler_specs):
|
||||||
# Write b_comps "on top of" a_comps.
|
# Write b_comps "on top of" a_comps.
|
||||||
spack.config.set('compilers', a_comps['compilers'], scope='low')
|
spack.config.set('compilers', a_comps['compilers'], scope='low')
|
||||||
spack.config.set('compilers', b_comps['compilers'], scope='high')
|
spack.config.set('compilers', b_comps['compilers'], scope='high')
|
||||||
@ -239,7 +240,7 @@ def test_write_key_to_disk(config, compiler_specs):
|
|||||||
check_compiler_config(b_comps['compilers'], *compiler_specs.b)
|
check_compiler_config(b_comps['compilers'], *compiler_specs.b)
|
||||||
|
|
||||||
|
|
||||||
def test_write_to_same_priority_file(config, compiler_specs):
|
def test_write_to_same_priority_file(mock_config, compiler_specs):
|
||||||
# Write b_comps in the same file as a_comps.
|
# Write b_comps in the same file as a_comps.
|
||||||
spack.config.set('compilers', a_comps['compilers'], scope='low')
|
spack.config.set('compilers', a_comps['compilers'], scope='low')
|
||||||
spack.config.set('compilers', b_comps['compilers'], scope='low')
|
spack.config.set('compilers', b_comps['compilers'], scope='low')
|
||||||
@ -260,7 +261,7 @@ def test_write_to_same_priority_file(config, compiler_specs):
|
|||||||
|
|
||||||
|
|
||||||
# repos
|
# repos
|
||||||
def test_write_list_in_memory(config):
|
def test_write_list_in_memory(mock_config):
|
||||||
spack.config.set('repos', repos_low['repos'], scope='low')
|
spack.config.set('repos', repos_low['repos'], scope='low')
|
||||||
spack.config.set('repos', repos_high['repos'], scope='high')
|
spack.config.set('repos', repos_high['repos'], scope='high')
|
||||||
|
|
||||||
@ -268,7 +269,7 @@ def test_write_list_in_memory(config):
|
|||||||
assert config == repos_high['repos'] + repos_low['repos']
|
assert config == repos_high['repos'] + repos_low['repos']
|
||||||
|
|
||||||
|
|
||||||
def test_substitute_config_variables(config):
|
def test_substitute_config_variables(mock_config):
|
||||||
prefix = spack.paths.prefix.lstrip('/')
|
prefix = spack.paths.prefix.lstrip('/')
|
||||||
|
|
||||||
assert os.path.join(
|
assert os.path.join(
|
||||||
@ -328,7 +329,7 @@ def test_substitute_config_variables(config):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.regression('7924')
|
@pytest.mark.regression('7924')
|
||||||
def test_merge_with_defaults(config, write_config_file):
|
def test_merge_with_defaults(mock_config, write_config_file):
|
||||||
"""This ensures that specified preferences merge with defaults as
|
"""This ensures that specified preferences merge with defaults as
|
||||||
expected. Originally all defaults were initialized with the
|
expected. Originally all defaults were initialized with the
|
||||||
exact same object, which led to aliasing problems. Therefore
|
exact same object, which led to aliasing problems. Therefore
|
||||||
@ -344,14 +345,14 @@ def test_merge_with_defaults(config, write_config_file):
|
|||||||
assert cfg['baz']['version'] == ['c']
|
assert cfg['baz']['version'] == ['c']
|
||||||
|
|
||||||
|
|
||||||
def test_substitute_user(config):
|
def test_substitute_user(mock_config):
|
||||||
user = getpass.getuser()
|
user = getpass.getuser()
|
||||||
assert '/foo/bar/' + user + '/baz' == canonicalize_path(
|
assert '/foo/bar/' + user + '/baz' == canonicalize_path(
|
||||||
'/foo/bar/$user/baz'
|
'/foo/bar/$user/baz'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_substitute_tempdir(config):
|
def test_substitute_tempdir(mock_config):
|
||||||
tempdir = tempfile.gettempdir()
|
tempdir = tempfile.gettempdir()
|
||||||
assert tempdir == canonicalize_path('$tempdir')
|
assert tempdir == canonicalize_path('$tempdir')
|
||||||
assert tempdir + '/foo/bar/baz' == canonicalize_path(
|
assert tempdir + '/foo/bar/baz' == canonicalize_path(
|
||||||
@ -359,12 +360,12 @@ def test_substitute_tempdir(config):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_read_config(config, write_config_file):
|
def test_read_config(mock_config, write_config_file):
|
||||||
write_config_file('config', config_low, 'low')
|
write_config_file('config', config_low, 'low')
|
||||||
assert spack.config.get('config') == config_low['config']
|
assert spack.config.get('config') == config_low['config']
|
||||||
|
|
||||||
|
|
||||||
def test_read_config_override_all(config, write_config_file):
|
def test_read_config_override_all(mock_config, write_config_file):
|
||||||
write_config_file('config', config_low, 'low')
|
write_config_file('config', config_low, 'low')
|
||||||
write_config_file('config', config_override_all, 'high')
|
write_config_file('config', config_override_all, 'high')
|
||||||
assert spack.config.get('config') == {
|
assert spack.config.get('config') == {
|
||||||
@ -372,7 +373,7 @@ def test_read_config_override_all(config, write_config_file):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_read_config_override_key(config, write_config_file):
|
def test_read_config_override_key(mock_config, write_config_file):
|
||||||
write_config_file('config', config_low, 'low')
|
write_config_file('config', config_low, 'low')
|
||||||
write_config_file('config', config_override_key, 'high')
|
write_config_file('config', config_override_key, 'high')
|
||||||
assert spack.config.get('config') == {
|
assert spack.config.get('config') == {
|
||||||
@ -381,7 +382,7 @@ def test_read_config_override_key(config, write_config_file):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_read_config_merge_list(config, write_config_file):
|
def test_read_config_merge_list(mock_config, write_config_file):
|
||||||
write_config_file('config', config_low, 'low')
|
write_config_file('config', config_low, 'low')
|
||||||
write_config_file('config', config_merge_list, 'high')
|
write_config_file('config', config_merge_list, 'high')
|
||||||
assert spack.config.get('config') == {
|
assert spack.config.get('config') == {
|
||||||
@ -390,7 +391,7 @@ def test_read_config_merge_list(config, write_config_file):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_read_config_override_list(config, write_config_file):
|
def test_read_config_override_list(mock_config, write_config_file):
|
||||||
write_config_file('config', config_low, 'low')
|
write_config_file('config', config_low, 'low')
|
||||||
write_config_file('config', config_override_list, 'high')
|
write_config_file('config', config_override_list, 'high')
|
||||||
assert spack.config.get('config') == {
|
assert spack.config.get('config') == {
|
||||||
@ -399,33 +400,33 @@ def test_read_config_override_list(config, write_config_file):
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def test_internal_config_update(config, write_config_file):
|
def test_internal_config_update(mock_config, write_config_file):
|
||||||
write_config_file('config', config_low, 'low')
|
write_config_file('config', config_low, 'low')
|
||||||
|
|
||||||
before = config.get('config')
|
before = mock_config.get('config')
|
||||||
assert before['install_tree'] == 'install_tree_path'
|
assert before['install_tree'] == 'install_tree_path'
|
||||||
|
|
||||||
# add an internal configuration scope
|
# add an internal configuration scope
|
||||||
scope = spack.config.InternalConfigScope('command_line')
|
scope = spack.config.InternalConfigScope('command_line')
|
||||||
assert 'InternalConfigScope' in repr(scope)
|
assert 'InternalConfigScope' in repr(scope)
|
||||||
|
|
||||||
config.push_scope(scope)
|
mock_config.push_scope(scope)
|
||||||
|
|
||||||
command_config = config.get('config', scope='command_line')
|
command_config = mock_config.get('config', scope='command_line')
|
||||||
command_config['install_tree'] = 'foo/bar'
|
command_config['install_tree'] = 'foo/bar'
|
||||||
|
|
||||||
config.set('config', command_config, scope='command_line')
|
mock_config.set('config', command_config, scope='command_line')
|
||||||
|
|
||||||
after = config.get('config')
|
after = mock_config.get('config')
|
||||||
assert after['install_tree'] == 'foo/bar'
|
assert after['install_tree'] == 'foo/bar'
|
||||||
|
|
||||||
|
|
||||||
def test_internal_config_filename(config, write_config_file):
|
def test_internal_config_filename(mock_config, write_config_file):
|
||||||
write_config_file('config', config_low, 'low')
|
write_config_file('config', config_low, 'low')
|
||||||
config.push_scope(spack.config.InternalConfigScope('command_line'))
|
mock_config.push_scope(spack.config.InternalConfigScope('command_line'))
|
||||||
|
|
||||||
with pytest.raises(NotImplementedError):
|
with pytest.raises(NotImplementedError):
|
||||||
config.get_config_filename('command_line', 'config')
|
mock_config.get_config_filename('command_line', 'config')
|
||||||
|
|
||||||
|
|
||||||
def test_mark_internal():
|
def test_mark_internal():
|
||||||
@ -597,7 +598,7 @@ def test_config_parse_list_in_dict(tmpdir):
|
|||||||
assert "mirrors.yaml:5" in str(e)
|
assert "mirrors.yaml:5" in str(e)
|
||||||
|
|
||||||
|
|
||||||
def test_bad_config_section(config):
|
def test_bad_config_section(mock_config):
|
||||||
"""Test that getting or setting a bad section gives an error."""
|
"""Test that getting or setting a bad section gives an error."""
|
||||||
with pytest.raises(spack.config.ConfigSectionError):
|
with pytest.raises(spack.config.ConfigSectionError):
|
||||||
spack.config.set('foobar', 'foobar')
|
spack.config.set('foobar', 'foobar')
|
||||||
@ -606,7 +607,7 @@ def test_bad_config_section(config):
|
|||||||
spack.config.get('foobar')
|
spack.config.get('foobar')
|
||||||
|
|
||||||
|
|
||||||
def test_bad_command_line_scopes(tmpdir, config):
|
def test_bad_command_line_scopes(tmpdir, mock_config):
|
||||||
cfg = spack.config.Configuration()
|
cfg = spack.config.Configuration()
|
||||||
|
|
||||||
with tmpdir.as_cwd():
|
with tmpdir.as_cwd():
|
||||||
@ -631,9 +632,9 @@ def test_add_command_line_scopes(tmpdir, mutable_config):
|
|||||||
with open(config_yaml, 'w') as f:
|
with open(config_yaml, 'w') as f:
|
||||||
f.write("""\
|
f.write("""\
|
||||||
config:
|
config:
|
||||||
verify_ssh: False
|
verify_ssl: False
|
||||||
dirty: False
|
dirty: False
|
||||||
"""'')
|
""")
|
||||||
|
|
||||||
spack.config._add_command_line_scopes(mutable_config, [str(tmpdir)])
|
spack.config._add_command_line_scopes(mutable_config, [str(tmpdir)])
|
||||||
|
|
||||||
@ -644,7 +645,7 @@ def test_immutable_scope(tmpdir):
|
|||||||
f.write("""\
|
f.write("""\
|
||||||
config:
|
config:
|
||||||
install_tree: dummy_tree_value
|
install_tree: dummy_tree_value
|
||||||
"""'')
|
""")
|
||||||
scope = spack.config.ImmutableConfigScope('test', str(tmpdir))
|
scope = spack.config.ImmutableConfigScope('test', str(tmpdir))
|
||||||
|
|
||||||
data = scope.get_section('config')
|
data = scope.get_section('config')
|
||||||
@ -654,6 +655,38 @@ def test_immutable_scope(tmpdir):
|
|||||||
scope.write_section('config')
|
scope.write_section('config')
|
||||||
|
|
||||||
|
|
||||||
|
def test_single_file_scope(tmpdir, config):
|
||||||
|
env_yaml = str(tmpdir.join("env.yaml"))
|
||||||
|
with open(env_yaml, 'w') as f:
|
||||||
|
f.write("""\
|
||||||
|
env:
|
||||||
|
config:
|
||||||
|
verify_ssl: False
|
||||||
|
dirty: False
|
||||||
|
packages:
|
||||||
|
libelf:
|
||||||
|
compiler: [ 'gcc@4.5.3' ]
|
||||||
|
repos:
|
||||||
|
- /x/y/z
|
||||||
|
""")
|
||||||
|
|
||||||
|
scope = spack.config.SingleFileScope(
|
||||||
|
'env', env_yaml, spack.schema.env.schema, ['env'])
|
||||||
|
|
||||||
|
with spack.config.override(scope):
|
||||||
|
# from the single-file config
|
||||||
|
assert spack.config.get('config:verify_ssl') is False
|
||||||
|
assert spack.config.get('config:dirty') is False
|
||||||
|
assert spack.config.get('packages:libelf:compiler') == ['gcc@4.5.3']
|
||||||
|
|
||||||
|
# from the lower config scopes
|
||||||
|
assert spack.config.get('config:checksum') is True
|
||||||
|
assert spack.config.get('config:checksum') is True
|
||||||
|
assert spack.config.get('packages:externalmodule:buildable') is False
|
||||||
|
assert spack.config.get('repos') == [
|
||||||
|
'/x/y/z', '$spack/var/spack/repos/builtin']
|
||||||
|
|
||||||
|
|
||||||
def check_schema(name, file_contents):
|
def check_schema(name, file_contents):
|
||||||
"""Check a Spack YAML schema against some data"""
|
"""Check a Spack YAML schema against some data"""
|
||||||
f = StringIO(file_contents)
|
f = StringIO(file_contents)
|
||||||
@ -661,6 +694,39 @@ def check_schema(name, file_contents):
|
|||||||
spack.config._validate(data, name)
|
spack.config._validate(data, name)
|
||||||
|
|
||||||
|
|
||||||
|
def test_good_env_yaml(tmpdir):
|
||||||
|
check_schema(spack.schema.env.schema, """\
|
||||||
|
spack:
|
||||||
|
config:
|
||||||
|
verify_ssl: False
|
||||||
|
dirty: False
|
||||||
|
repos:
|
||||||
|
- ~/my/repo/location
|
||||||
|
mirrors:
|
||||||
|
remote: /foo/bar/baz
|
||||||
|
compilers:
|
||||||
|
- compiler:
|
||||||
|
spec: cce@2.1
|
||||||
|
operating_system: cnl
|
||||||
|
modules: []
|
||||||
|
paths:
|
||||||
|
cc: /path/to/cc
|
||||||
|
cxx: /path/to/cxx
|
||||||
|
fc: /path/to/fc
|
||||||
|
f77: /path/to/f77
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
|
def test_bad_env_yaml(tmpdir):
|
||||||
|
with pytest.raises(spack.config.ConfigFormatError):
|
||||||
|
check_schema(spack.schema.env.schema, """\
|
||||||
|
env:
|
||||||
|
foobar:
|
||||||
|
verify_ssl: False
|
||||||
|
dirty: False
|
||||||
|
""")
|
||||||
|
|
||||||
|
|
||||||
def test_bad_config_yaml(tmpdir):
|
def test_bad_config_yaml(tmpdir):
|
||||||
with pytest.raises(spack.config.ConfigFormatError):
|
with pytest.raises(spack.config.ConfigFormatError):
|
||||||
check_schema(spack.schema.config.schema, """\
|
check_schema(spack.schema.config.schema, """\
|
||||||
|
Loading…
Reference in New Issue
Block a user