1f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# Copyright 2013 The Chromium Authors. All rights reserved. 2f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be 3f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# found in the LICENSE file. 4f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 5f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)"""Configuration variable management for the cr tool. 6f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 7f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)This holds the classes that support the hierarchical variable management used 8f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)in the cr tool to provide all the command configuration controls. 9f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)""" 10f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 11f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import string 12f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 13f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)import cr.visitor 14f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 15f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)_PARSE_CONSTANT_VALUES = [None, True, False] 16f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)_PARSE_CONSTANTS = dict((str(value), value) for value in _PARSE_CONSTANT_VALUES) 17f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 18f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# GLOBALS is the singleton used to tie static global configuration objects 19f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)# together. 20f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)GLOBALS = [] 21f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 22f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 23f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class _MissingToErrorFormatter(string.Formatter): 24f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """A string formatter used in value resolve. 25f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 26f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) The main extra it adds is a new conversion specifier 'e' that throws a 27f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) KeyError if it could not find the value. 28f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) This allows a string value to use {A_KEY!e} to indicate that it is a 29f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) formatting error if A_KEY is not present. 30f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 31f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 32f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def convert_field(self, value, conversion): 33f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if conversion == 'e': 34f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) result = str(value) 35f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if not result: 36f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) raise KeyError('unknown') 37f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return result 38f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return super(_MissingToErrorFormatter, self).convert_field( 39f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value, conversion) 40f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 41f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 42f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class _Tracer(object): 43f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Traces variable lookups. 44f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 45f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) This adds a hook to a config object, and uses it to track all variable 46f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) lookups that happen and add them to a trail. When done, it removes the hook 47f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) again. This is used to provide debugging information about what variables are 48f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) used in an operation. 49f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 50f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 51f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def __init__(self, config): 52f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.config = config 53f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.trail = [] 54f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 55f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def __enter__(self): 56f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.config.fixup_hooks.append(self._Trace) 57f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self 58f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 59f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def __exit__(self, *_): 60f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.config.fixup_hooks.remove(self._Trace) 61f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.config.trail = self.trail 62f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return False 63f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 64f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def _Trace(self, _, key, value): 65f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.trail.append((key, value)) 66f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return value 67f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 68f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 69f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles)class Config(cr.visitor.Node): 70f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """The main variable holding class. 71f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 72f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) This holds a set of unresolved key value pairs, and the set of child Config 73f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) objects that should be referenced when looking up a key. 74f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Key search is one in a pre-order traversal, and new children are prepended. 75f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) This means parents override children, and the most recently added child 76f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) overrides the rest. 77f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 78f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Values can be simple python types, callable dynamic values, or strings. 79f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) If the value is a string, it is assumed to be a standard python format string 80f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) where the root config object is used to resolve the keys. This allows values 81f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) to refer to variables that are overriden in another part of the hierarchy. 82f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 83f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 84f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @classmethod 85f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def From(cls, *args, **kwargs): 86f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Builds an unnamed config object from a set of key,value args.""" 87f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return Config('??').Apply(args, kwargs) 88f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 89f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @classmethod 90f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def If(cls, condition, true_value, false_value=''): 91f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Returns a config value that selects a value based on the condition. 92f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 93f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Args: 94f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) condition: The variable name to select a value on. 95f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) true_value: The value to use if the variable is True. 96f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) false_value: The value to use if the resolved variable is False. 97f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Returns: 98f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) A dynamic value. 99f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 100f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Resolve(context): 101f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) test = context.Get(condition) 102f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if test: 103f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value = true_value 104f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) else: 105f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value = false_value 106f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return context.Substitute(value) 107f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return Resolve 108f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 109f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @classmethod 110f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Optional(cls, value, alternate=''): 111f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Returns a dynamic value that defaults to an alternate. 112f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 113f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Args: 114f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value: The main value to resolve. 115f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) alternate: The value to use if the main value does not resolve. 116f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Returns: 117f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value if it resolves, alternate otherwise. 118f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 119f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Resolve(context): 120f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 121f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return context.Substitute(value) 122f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) except KeyError: 123f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return context.Substitute(alternate) 124f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return Resolve 125f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 126f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def __init__(self, name='--', literal=False, export=None, enabled=True): 127f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) super(Config, self).__init__(name=name, enabled=enabled, export=export) 128f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self._literal = literal 129f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self._formatter = _MissingToErrorFormatter() 130f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.fixup_hooks = [] 131f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.trail = [] 132f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 133f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @property 134f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def literal(self): 135f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self._literal 136f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 137f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Substitute(self, value): 138f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self._formatter.vformat(str(value), (), self) 139f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 140f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Resolve(self, visitor, key, value): 141f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Resolves a value to it's final form. 142f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 143f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Raw values can be callable, simple values, or contain format strings. 144f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Args: 145f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) visitor: The vistior asking to resolve a value. 146f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) key: The key being visited. 147f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value: The unresolved value associated with the key. 148f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Returns: 149f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) the fully resolved value. 150f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 151f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) error = None 152f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if callable(value): 153f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value = value(self) 154f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # Using existence of value.swapcase as a proxy for is a string 155f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) elif hasattr(value, 'swapcase'): 156f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if not visitor.current_node.literal: 157f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 158f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value = self.Substitute(value) 159f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) except KeyError as e: 160f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) error = e 161f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self.Fixup(key, value), error 162f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 163f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Fixup(self, key, value): 164f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for hook in self.fixup_hooks: 165f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value = hook(self, key, value) 166f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return value 167f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 168f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) @staticmethod 169f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def ParseValue(value): 170f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Converts a string to a value. 171f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 172f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Takes a string from something like an environment variable, and tries to 173f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) build an internal typed value. Recognizes Null, booleans, and numbers as 174f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) special. 175f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Args: 176f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) value: The the string value to interpret. 177f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Returns: 178f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) the parsed form of the value. 179f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 180f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if value in _PARSE_CONSTANTS: 181f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return _PARSE_CONSTANTS[value] 182f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 183f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return int(value) 184f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) except ValueError: 185f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) pass 186f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) try: 187f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return float(value) 188f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) except ValueError: 189f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) pass 190f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return value 191f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 192f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def _Set(self, key, value): 193f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) # early out if the value did not change, so we don't call change callbacks 194f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if value == self._values.get(key, None): 195f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return 196f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self._values[key] = value 197f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.NotifyChanged() 198f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self 199f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 200f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def ApplyMap(self, arg): 201f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) for key, value in arg.items(): 202f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self._Set(key, value) 203f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self 204f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 205f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Apply(self, args, kwargs): 206f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """Bulk set variables from arguments. 207f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 208f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Intended for internal use by the Set and From methods. 209f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Args: 210f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) args: must be either a dict or something that can build a dict. 211f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) kwargs: must be a dict. 212f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) Returns: 213f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self for easy chaining. 214f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) """ 215f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if len(args) == 1: 216f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) arg = args[0] 217f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) if isinstance(arg, dict): 218f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.ApplyMap(arg) 219f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) else: 220f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.ApplyMap(dict(arg)) 221f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) elif len(args) > 1: 222f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.ApplyMap(dict(args)) 223f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self.ApplyMap(kwargs) 224f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self 225f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 226f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Set(self, *args, **kwargs): 227f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self.Apply(args, kwargs) 228f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 229f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def Trace(self): 230f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return _Tracer(self) 231f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 232f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def __getitem__(self, key): 233f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) return self.Get(key) 234f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) 235f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) def __setitem__(self, key, value): 236f2477e01787aa58f445919b809d89e252beef54fTorne (Richard Coles) self._Set(key, value) 237a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 238a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) def __contains__(self, key): 239a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) return self.Find(key) is not None 240a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles) 241