env: make spack config edit and spack config get environment-aware

- with no arguments, these commands will now edit or dump the
  environment's `spack.yaml` file.

- users may not know where named environments live

- this makes it convenient for users to get to the spack.yaml
  configuration file for their named environment.
This commit is contained in:
Todd Gamblin 2018-11-07 18:43:35 -08:00 committed by Peter Scheibel
parent 25f8abb963
commit 8d92fd6640
5 changed files with 182 additions and 26 deletions

View File

@ -3,7 +3,13 @@
#
# SPDX-License-Identifier: (Apache-2.0 OR MIT)
from __future__ import print_function
import os
import llnl.util.tty as tty
import spack.config
import spack.environment as ev
from spack.util.editor import editor
@ -27,6 +33,7 @@ def setup_parser(subparser):
get_parser.add_argument('section',
help="configuration section to print. "
"options: %(choices)s",
nargs='?',
metavar='SECTION',
choices=spack.config.section_schemas)
@ -43,29 +50,81 @@ def setup_parser(subparser):
help="configuration section to edit. "
"options: %(choices)s",
metavar='SECTION',
nargs='?',
choices=spack.config.section_schemas)
edit_parser.add_argument(
'--print-file', action='store_true',
help="print the file name that would be edited")
def _get_scope_and_section(args):
"""Extract config scope and section from arguments."""
scope = args.scope
section = args.section
# w/no args and an active environment, point to env manifest
if not args.section:
env = ev.get_env(args, 'config edit', required=False)
if env:
scope = env.env_file_config_scope_name()
# set scope defaults
elif not args.scope:
if section == 'compilers':
scope = spack.config.default_modify_scope()
else:
scope = 'user'
return scope, section
def config_get(args):
spack.config.config.print_section(args.section)
"""Dump merged YAML configuration for a specific section.
With no arguments and an active environment, print the contents of
the environment's manifest file (spack.yaml).
"""
scope, section = _get_scope_and_section(args)
if scope and scope.startswith('env:'):
config_file = spack.config.config.get_config_filename(scope, section)
if os.path.exists(config_file):
with open(config_file) as f:
print(f.read())
else:
tty.die('environment has no %s file' % ev.manifest_name)
elif section is not None:
spack.config.config.print_section(section)
else:
tty.die('`spack config get` requires a section argument '
'or an active environment.')
def config_blame(args):
"""Print out line-by-line blame of merged YAML."""
spack.config.config.print_section(args.section, blame=True)
def config_edit(args):
if not args.scope:
if args.section == 'compilers':
args.scope = spack.config.default_modify_scope()
else:
args.scope = 'user'
if not args.section:
args.section = None
"""Edit the configuration file for a specific scope and config section.
config = spack.config.config
config_file = config.get_config_filename(args.scope, args.section)
editor(config_file)
With no arguments and an active environment, edit the spack.yaml for
the active environment.
"""
scope, section = _get_scope_and_section(args)
if not scope and not section:
tty.die('`spack config edit` requires a section argument '
'or an active environment.')
config_file = spack.config.config.get_config_filename(scope, section)
if args.print_file:
print(config_file)
else:
editor(config_file)
def config(parser, args):

View File

@ -483,9 +483,13 @@ def included_config_scopes(self):
return scopes
def env_file_config_scope_name(self):
"""Name of the config scope of this environment's manifest file."""
return 'env:%s' % self.name
def env_file_config_scope(self):
"""Get the configuration scope for the environment's manifest file."""
config_name = 'env:%s' % self.name
config_name = self.env_file_config_scope_name()
return spack.config.SingleFileScope(config_name,
self.manifest_path,
spack.schema.env.schema,

View File

@ -0,0 +1,93 @@
# 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)
import os
from llnl.util.filesystem import mkdirp
import spack.config
import spack.environment as ev
from spack.main import SpackCommand
config = SpackCommand('config')
def test_get_config_scope(mock_config):
assert config('get', 'compilers').strip() == 'compilers: {}'
def test_get_config_scope_merged(mock_config):
low_path = mock_config.scopes['low'].path
high_path = mock_config.scopes['high'].path
mkdirp(low_path)
mkdirp(high_path)
with open(os.path.join(low_path, 'repos.yaml'), 'w') as f:
f.write('''\
repos:
- repo3
''')
with open(os.path.join(high_path, 'repos.yaml'), 'w') as f:
f.write('''\
repos:
- repo1
- repo2
''')
assert config('get', 'repos').strip() == '''repos:
- repo1
- repo2
- repo3'''
def test_config_edit():
"""Ensure `spack config edit` edits the right paths."""
dms = spack.config.default_modify_scope()
dms_path = spack.config.config.scopes[dms].path
user_path = spack.config.config.scopes['user'].path
comp_path = os.path.join(dms_path, 'compilers.yaml')
repos_path = os.path.join(user_path, 'repos.yaml')
assert config('edit', '--print-file', 'compilers').strip() == comp_path
assert config('edit', '--print-file', 'repos').strip() == repos_path
def test_config_get_gets_spack_yaml(mutable_mock_env_path):
env = ev.create('test')
config('get', fail_on_error=False)
assert config.returncode == 1
with env:
config('get', fail_on_error=False)
assert config.returncode == 1
env.write()
assert 'mpileaks' not in config('get')
env.add('mpileaks')
env.write()
assert 'mpileaks' in config('get')
def test_config_edit_edits_spack_yaml(mutable_mock_env_path):
env = ev.create('test')
with env:
assert config('edit', '--print-file').strip() == env.manifest_path
def test_config_edit_fails_correctly_with_no_env(mutable_mock_env_path):
output = config('edit', '--print-file', fail_on_error=False)
assert "requires a section argument or an active environment" in output
def test_config_get_fails_correctly_with_no_env(mutable_mock_env_path):
output = config('get', fail_on_error=False)
assert "requires a section argument or an active environment" in output

View File

@ -49,20 +49,6 @@
'build_stage:': ['patha', 'pathb']}}
@pytest.fixture()
def mock_config(tmpdir):
"""Mocks the configuration scope."""
real_configuration = spack.config.config
spack.config.config = spack.config.Configuration(
*[spack.config.ConfigScope(name, str(tmpdir.join(name)))
for name in ['low', 'high']])
yield spack.config.config
spack.config.config = real_configuration
@pytest.fixture()
def write_config_file(tmpdir):
"""Returns a function that writes a config file."""

View File

@ -309,6 +309,20 @@ def mutable_config(tmpdir_factory, configuration_dir, config):
spack.package_prefs.PackagePrefs.clear_caches()
@pytest.fixture()
def mock_config(tmpdir):
"""Mocks two configuration scopes: 'low' and 'high'."""
real_configuration = spack.config.config
spack.config.config = spack.config.Configuration(
*[spack.config.ConfigScope(name, str(tmpdir.join(name)))
for name in ['low', 'high']])
yield spack.config.config
spack.config.config = real_configuration
def _populate(mock_db):
r"""Populate a mock database with packages.