| Index: third_party/recipe_engine/field_composer.py
|
| diff --git a/third_party/recipe_engine/field_composer.py b/third_party/recipe_engine/field_composer.py
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..37c72934c69a7e763274ff0f9b4c7be68481e9a0
|
| --- /dev/null
|
| +++ b/third_party/recipe_engine/field_composer.py
|
| @@ -0,0 +1,86 @@
|
| +# Copyright 2013-2015 The Chromium Authors. All rights reserved.
|
| +# Use of this source code is governed by a BSD-style license that can be
|
| +# found in the LICENSE file.
|
| +
|
| +
|
| +class FieldComposerError(BaseException):
|
| + """Base error class for this module."""
|
| + pass
|
| +
|
| +
|
| +class RegistryConflict(FieldComposerError):
|
| + pass
|
| +
|
| +
|
| +class DegenerateRegistryError(FieldComposerError):
|
| + pass
|
| +
|
| +
|
| +class CompositionUndefined(FieldComposerError):
|
| + pass
|
| +
|
| +
|
| +class FieldComposer(object):
|
| +
|
| + def __init__(self, fields, registered_functors):
|
| + """Initialize the internal registry mapping names to functors."""
|
| + try:
|
| + self._registry = {
|
| + name: {'combine': value['combine']}
|
| + for name, value in registered_functors.iteritems()}
|
| + except KeyError:
|
| + raise DegenerateRegistryError(
|
| + 'Registry entries must contain key "combine."')
|
| + self._fields = fields
|
| +
|
| + def __contains__(self, key):
|
| + return key in self._fields
|
| +
|
| + def __getitem__(self, key):
|
| + return self._fields[key]
|
| +
|
| + def get(self, index, default=None):
|
| + """Wrapper for self._fields.get."""
|
| + return self._fields.get(index, default)
|
| +
|
| + def iteritems(self):
|
| + """Wrapper for self._fields.iteritems."""
|
| + return self._fields.iteritems()
|
| +
|
| + def compose(self, second_compositor):
|
| + """Return the monoidal composition of two FieldComposers."""
|
| + # If second_compositor is a FieldComposer, registries shouldn't conflict.
|
| + if isinstance(second_compositor, dict):
|
| + second_compositor = FieldComposer(second_compositor, {})
|
| + new_registry = self._registry.copy()
|
| + second_registry = second_compositor._registry
|
| + for key, value in second_registry.iteritems():
|
| + if key in self._registry and value != self._registry[key]:
|
| + raise RegistryConflict('Conflicting values for key %s.' % key)
|
| + new_registry[key] = value
|
| +
|
| + # populate new field dictionary with composed values
|
| + all_keys = set().union(self._fields, second_compositor._fields)
|
| + new_fields = {}
|
| + for name in all_keys:
|
| + if name not in new_registry:
|
| + raise CompositionUndefined(
|
| + "No combine function registered for %s." % name)
|
| + if name in second_compositor:
|
| + new_value = self.get_with_context(name, second_compositor[name])
|
| + else:
|
| + # Name is in exactly one compositor, so get that value.
|
| + new_value = self[name]
|
| + new_fields[name] = new_value
|
| +
|
| + # create and return the new compositor
|
| + return FieldComposer(new_fields, new_registry)
|
| +
|
| + def get_with_context(self, key, value):
|
| + """Combine the current value for key (if any) with value."""
|
| + if key not in self._registry:
|
| + raise CompositionUndefined(
|
| + "No combine function registered for %s." % key)
|
| + if key in self:
|
| + return self._registry[key]['combine'](self.get(key), value)
|
| + return value
|
|
|