summaryrefslogtreecommitdiff
path: root/lib/spack/spack/config.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/spack/spack/config.py')
-rw-r--r--lib/spack/spack/config.py69
1 files changed, 67 insertions, 2 deletions
diff --git a/lib/spack/spack/config.py b/lib/spack/spack/config.py
index a3d8101cad..425fcec8ee 100644
--- a/lib/spack/spack/config.py
+++ b/lib/spack/spack/config.py
@@ -30,6 +30,7 @@ schemas are in submodules of :py:mod:`spack.schema`.
"""
+import collections
import copy
import os
import re
@@ -352,6 +353,7 @@ class Configuration(object):
self.scopes = OrderedDict()
for scope in scopes:
self.push_scope(scope)
+ self.format_updates = collections.defaultdict(list)
def push_scope(self, scope):
"""Add a higher precedence scope to the Configuration."""
@@ -440,7 +442,7 @@ class Configuration(object):
for scope in self.scopes.values():
scope.clear()
- def update_config(self, section, update_data, scope=None):
+ def update_config(self, section, update_data, scope=None, force=False):
"""Update the configuration file for a particular scope.
Overwrites contents of a section in a scope with update_data,
@@ -449,7 +451,26 @@ class Configuration(object):
update_data should have the top-level section name stripped off
(it will be re-added). Data itself can be a list, dict, or any
other yaml-ish structure.
+
+ Configuration scopes that are still written in an old schema
+ format will fail to update unless ``force`` is True.
+
+ Args:
+ section (str): section of the configuration to be updated
+ update_data (dict): data to be used for the update
+ scope (str): scope to be updated
+ force (str): force the update
"""
+ if self.format_updates.get(section) and not force:
+ msg = ('The "{0}" section of the configuration needs to be written'
+ ' to disk, but is currently using a deprecated format. '
+ 'Please update it using:\n\n'
+ '\tspack config [--scope=<scope] update {0}\n\n'
+ 'Note that previous versions of Spack will not be able to '
+ 'use the updated configuration.')
+ msg = msg.format(section)
+ raise RuntimeError(msg)
+
_validate_section_name(section) # validate section name
scope = self._validate_scope(scope) # get ConfigScope object
@@ -514,6 +535,15 @@ class Configuration(object):
if section not in data:
continue
+ # We might be reading configuration files in an old format,
+ # thus read data and update it in memory if need be.
+ changed = _update_in_memory(data, section)
+ if changed:
+ self.format_updates[section].append(scope)
+ msg = ('OUTDATED CONFIGURATION FILE '
+ '[section={0}, scope={1}, dir={2}]')
+ tty.debug(msg.format(section, scope.name, scope.path))
+
merged_section = merge_yaml(merged_section, data)
# no config files -- empty config.
@@ -723,7 +753,7 @@ def get(path, default=None, scope=None):
def set(path, value, scope=None):
- """Convenience function for getting single values in config files.
+ """Convenience function for setting single values in config files.
Accepts the path syntax described in ``get()``.
"""
@@ -999,6 +1029,41 @@ def default_list_scope():
return None
+def _update_in_memory(data, section):
+ """Update the format of the configuration data in memory.
+
+ This function assumes the section is valid (i.e. validation
+ is responsibility of the caller)
+
+ Args:
+ data (dict): configuration data
+ section (str): section of the configuration to update
+
+ Returns:
+ True if the data was changed, False otherwise
+ """
+ update_fn = ensure_latest_format_fn(section)
+ changed = update_fn(data[section])
+ return changed
+
+
+def ensure_latest_format_fn(section):
+ """Return a function that takes as input a dictionary read from
+ a configuration file and update it to the latest format.
+
+ The function returns True if there was any update, False otherwise.
+
+ Args:
+ section (str): section of the configuration e.g. "packages",
+ "config", etc.
+ """
+ # The line below is based on the fact that every module we need
+ # is already imported at the top level
+ section_module = getattr(spack.schema, section)
+ update_fn = getattr(section_module, 'update', lambda x: False)
+ return update_fn
+
+
class ConfigError(SpackError):
"""Superclass for all Spack config related errors."""