OLD | NEW |
| (Empty) |
1 # Copyright 2013-2015 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 | |
6 class FieldComposerError(BaseException): | |
7 """Base error class for this module.""" | |
8 pass | |
9 | |
10 | |
11 class RegistryConflict(FieldComposerError): | |
12 pass | |
13 | |
14 | |
15 class DegenerateRegistryError(FieldComposerError): | |
16 pass | |
17 | |
18 | |
19 class CompositionUndefined(FieldComposerError): | |
20 pass | |
21 | |
22 | |
23 class FieldComposer(object): | |
24 | |
25 def __init__(self, fields, registered_functors): | |
26 """Initialize the internal registry mapping names to functors.""" | |
27 try: | |
28 self._registry = { | |
29 name: {'combine': value['combine']} | |
30 for name, value in registered_functors.iteritems()} | |
31 except KeyError: | |
32 raise DegenerateRegistryError( | |
33 'Registry entries must contain key "combine."') | |
34 self._fields = fields | |
35 | |
36 def __contains__(self, key): | |
37 return key in self._fields | |
38 | |
39 def __getitem__(self, key): | |
40 return self._fields[key] | |
41 | |
42 def get(self, index, default=None): | |
43 """Wrapper for self._fields.get.""" | |
44 return self._fields.get(index, default) | |
45 | |
46 def iteritems(self): | |
47 """Wrapper for self._fields.iteritems.""" | |
48 return self._fields.iteritems() | |
49 | |
50 def compose(self, second_compositor): | |
51 """Return the monoidal composition of two FieldComposers.""" | |
52 # If second_compositor is a FieldComposer, registries shouldn't conflict. | |
53 if isinstance(second_compositor, dict): | |
54 second_compositor = FieldComposer(second_compositor, {}) | |
55 new_registry = self._registry.copy() | |
56 second_registry = second_compositor._registry | |
57 for key, value in second_registry.iteritems(): | |
58 if key in self._registry and value != self._registry[key]: | |
59 raise RegistryConflict('Conflicting values for key %s.' % key) | |
60 new_registry[key] = value | |
61 | |
62 # populate new field dictionary with composed values | |
63 all_keys = set().union(self._fields, second_compositor._fields) | |
64 new_fields = {} | |
65 for name in all_keys: | |
66 if name not in new_registry: | |
67 raise CompositionUndefined( | |
68 "No combine function registered for %s." % name) | |
69 if name in second_compositor: | |
70 new_value = self.get_with_context(name, second_compositor[name]) | |
71 else: | |
72 # Name is in exactly one compositor, so get that value. | |
73 new_value = self[name] | |
74 new_fields[name] = new_value | |
75 | |
76 # create and return the new compositor | |
77 return FieldComposer(new_fields, new_registry) | |
78 | |
79 def get_with_context(self, key, value): | |
80 """Combine the current value for key (if any) with value.""" | |
81 if key not in self._registry: | |
82 raise CompositionUndefined( | |
83 "No combine function registered for %s." % key) | |
84 if key in self: | |
85 return self._registry[key]['combine'](self.get(key), value) | |
86 return value | |
OLD | NEW |