2887
2887
_NewlyCreatedOption = object()
2888
2888
"""Was the option created during the MutableSection lifetime"""
2889
_DeletedOption = object()
2890
"""Was the option deleted during the MutableSection lifetime"""
2891
2893
class MutableSection(Section):
2894
2896
def __init__(self, section_id, options):
2895
2897
super(MutableSection, self).__init__(section_id, options)
2898
self.reset_changes()
2898
2900
def set(self, name, value):
2899
2901
if name not in self.options:
2908
2910
self.orig[name] = self.get(name, None)
2909
2911
del self.options[name]
2913
def reset_changes(self):
2916
def apply_changes(self, dirty, store):
2917
"""Apply option value changes.
2919
``self`` has been reloaded from the persistent storage. ``dirty``
2920
contains the changes made since the previous loading.
2922
:param dirty: the mutable section containing the changes.
2924
:param store: the store containing the section
2926
for k, expected in dirty.orig.iteritems():
2927
actual = dirty.get(k, _DeletedOption)
2928
reloaded = self.get(k, _NewlyCreatedOption)
2929
if actual is _DeletedOption:
2930
if k in self.options:
2934
# Report concurrent updates in an ad-hoc way. This should only
2935
# occurs when different processes try to update the same option
2936
# which is not supported (as in: the config framework is not meant
2937
# to be used a sharing mechanism).
2938
if expected != reloaded:
2939
if actual is _DeletedOption:
2940
actual = '<DELETED>'
2941
if reloaded is _NewlyCreatedOption:
2942
reloaded = '<CREATED>'
2943
if expected is _NewlyCreatedOption:
2944
expected = '<CREATED>'
2945
# Someone changed the value since we get it from the persistent
2947
trace.warning(gettext(
2948
"Option {0} in section {1} of {2} was changed"
2949
" from {3} to {4}. The {5} value will be saved.".format(
2950
k, self.id, store.external_url(), expected,
2952
# No need to keep track of these changes
2953
self.reset_changes()
2912
2956
class Store(object):
2913
2957
"""Abstract interface to persistent storage for configuration options."""
2961
3009
"""Saves the Store to persistent storage."""
2962
3010
raise NotImplementedError(self.save)
3012
def _need_saving(self):
3013
for s in self.dirty_sections:
3015
# At least one dirty section contains a modification
3019
def apply_changes(self, dirty_sections):
3020
"""Apply changes from dirty sections while checking for coherency.
3022
The Store content is discarded and reloaded from persistent storage to
3023
acquire up-to-date values.
3025
Dirty sections are MutableSection which kept track of the value they
3026
are expected to update.
3028
# We need an up-to-date version from the persistent storage, unload the
3029
# store. The reload will occur when needed (triggered by the first
3030
# get_mutable_section() call below.
3032
# Apply the changes from the preserved dirty sections
3033
for dirty in dirty_sections:
3034
clean = self.get_mutable_section(dirty.id)
3035
clean.apply_changes(dirty, self)
3036
# Everything is clean now
3037
self.dirty_sections = []
3039
def save_changes(self):
3040
"""Saves the Store to persistent storage if changes occurred.
3042
Apply the changes recorded in the mutable sections to a store content
3043
refreshed from persistent storage.
3045
raise NotImplementedError(self.save_changes)
2964
3047
def external_url(self):
2965
3048
raise NotImplementedError(self.external_url)
3087
3171
except UnicodeDecodeError:
3088
3172
raise errors.ConfigContentError(self.external_url())
3174
def save_changes(self):
3175
if not self.is_loaded():
3178
if not self._need_saving():
3180
# Preserve the current version
3181
current = self._config_obj
3182
dirty_sections = list(self.dirty_sections)
3183
self.apply_changes(dirty_sections)
3184
# Save to the persistent storage
3090
3187
def save(self):
3091
3188
if not self.is_loaded():
3092
3189
# Nothing to save
3127
3224
section = self._config_obj
3129
3226
section = self._config_obj.setdefault(section_id, {})
3130
return self.mutable_section_class(section_id, section)
3227
mutable_section = self.mutable_section_class(section_id, section)
3228
# All mutable sections can become dirty
3229
self.dirty_sections.append(mutable_section)
3230
return mutable_section
3132
3232
def quote(self, value):