def test_set_value_escaped(self): v = 42 l = ['a', 'b:b', 'c'] d = {'a':{'b:b':{'c':v}}} p = DictPath(':', l) p.set_value(d, v+1) self.assertEqual(d['a']['b:b']['c'], v+1)
def test_set_value_escaped_listindex_list(self): v = 42 l = ["a", 1, "c"] d = {"a": [None, {"c": v}, None]} p = DictPath(":", l) p.set_value(d, v + 1) self.assertEqual(d["a"][1]["c"], v + 1)
def test_set_value_escaped_listindex_str(self): v = 42 s = "a:1:c" d = {"a": [None, {"c": v}, None]} p = DictPath(":", s) p.set_value(d, v + 1) self.assertEqual(d["a"][1]["c"], v + 1)
def test_set_value_escaped_listindex_list(self): v = 42 l = ['a', 1, 'c'] d = {'a':[None, {'c':v}, None]} p = DictPath(':', l) p.set_value(d, v+1) self.assertEqual(d['a'][1]['c'], v+1)
def test_set_value_escaped(self): v = 42 l = ["a", "b:b", "c"] d = {"a": {"b:b": {"c": v}}} p = DictPath(":", l) p.set_value(d, v + 1) self.assertEqual(d["a"]["b:b"]["c"], v + 1)
def test_set_value_escaped_listindex_list(self): v = 42 l = ['a', 1, 'c'] d = {'a': [None, {'c': v}, None]} p = DictPath(':', l) p.set_value(d, v + 1) self.assertEqual(d['a'][1]['c'], v + 1)
def test_set_value_escaped_listindex_str(self): v = 42 s = 'a:1:c' d = {'a':[None, {'c':v}, None]} p = DictPath(':', s) p.set_value(d, v+1) self.assertEqual(d['a'][1]['c'], v+1)
def test_set_value_escaped(self): v = 42 l = ['a', 'b:b', 'c'] d = {'a': {'b:b': {'c': v}}} p = DictPath(':', l) p.set_value(d, v + 1) self.assertEqual(d['a']['b:b']['c'], v + 1)
def test_set_value_escaped_listindex_str(self): v = 42 s = 'a:1:c' d = {'a': [None, {'c': v}, None]} p = DictPath(':', s) p.set_value(d, v + 1) self.assertEqual(d['a'][1]['c'], v + 1)
def test_equality(self): delim = ':' s = 'a{0}b{0}c'.format(delim) l = ['a', 'b', 'c'] p1 = DictPath(delim, s) p2 = DictPath(delim, l) self.assertEqual(p1, p2)
def test_inequality_content(self): delim = ':' s = 'a{0}b{0}c'.format(delim) l = ['d', 'e', 'f'] p1 = DictPath(delim, s) p2 = DictPath(delim, l) self.assertNotEqual(p1, p2)
def merge(self, other): """Merge function (public edition). Call _merge_recurse on self with either another Parameter object or a dict (for initialization). Set initmerge if it's a dict. Args: other (dict or Parameter): Thing to merge with self._base Returns: None: Nothing """ self._unrendered = None if isinstance(other, dict): wrapped = self._wrap_dict(other, DictPath(self._settings.delimiter)) elif isinstance(other, self.__class__): wrapped = self._wrap_dict(other._base, DictPath(self._settings.delimiter)) else: raise TypeError('Cannot merge %s objects into %s' % (type(other), self.__class__.__name__)) self._base = self._merge_recurse(self._base, wrapped, DictPath(self._settings.delimiter))
def _value_expression(self, inventory): results = {} path = DictPath(self._delimiter, self._expr[0][1]).drop_first() for node, items in inventory.iteritems(): if path.exists_in(items): results[node] = copy.deepcopy(self._resolve(path, items)) return results
def _get_required_paths(self, mainpath): paths = {} path = DictPath(self._settings.delimiter) for i in mainpath.key_parts(): path.add_subpath(i) if path in self._unrendered: paths[path] = True for i in self._unrendered: if mainpath.is_ancestor_of(i) or mainpath == i: paths[i] = True return paths
def _merge_recurse(self, cur, new, path=None, initmerge=False): """Merge a parameter with another parameter. Iterate over keys in new. Call _merge_dict, _extend_list, or _update_scalar depending on type. Pass along whether this is an initialization merge. Args: cur (dict): Current dictionary new (dict): Dictionary to be merged path (string): Merging path from recursion initmerge (bool): True if called as part of entity init, defaults to False Returns: dict: a merged dictionary """ if path is None: path = DictPath(self.delimiter) if isinstance(new, dict): if cur is None: cur = {} return self._merge_dict(cur, new, path, initmerge) elif isinstance(new, list): if cur is None: cur = [] return self._extend_list(cur, new, path) else: return self._update_scalar(cur, new, path)
def _initialise_interpolate(self): if self._unrendered is None: self._unrendered = {} self._inv_queries = [] self._needs_all_envs = False self._resolve_errors = ResolveErrorList() self._render_simple_dict(self._base, DictPath(self._settings.delimiter))
def _get_vars(self, var, export, parameter, value): if isinstance(var, str): path = DictPath(self._delimiter, var) if path.path[0].lower() == 'exports': export = path elif path.path[0].lower() == 'self': parameter = path else: value = var else: value = var return export, parameter, value
def _interpolate_references(self, path, value, inventory): all_refs = False while not all_refs: for ref in value.get_references(): path_from_ref = DictPath(self._delimiter, ref) if path_from_ref in self._unrendered: if self._unrendered[path_from_ref] is False: # every call to _interpolate_inner replaces the value of # self._unrendered[path] with False # Therefore, if we encounter False instead of True, # it means that we have already processed it and are now # faced with a cyclical reference. raise InfiniteRecursionError(path, ref) else: self._interpolate_inner(path_from_ref, inventory) else: # ensure ancestor keys are already dereferenced ancestor = DictPath(self._delimiter) for k in path_from_ref.key_parts(): ancestor = ancestor.new_subpath(k) if ancestor in self._unrendered: self._interpolate_inner(ancestor, inventory) if value.allRefs(): all_refs = True else: # not all references in the value could be calculated previously so # try recalculating references with current context and recursively # call _interpolate_inner if the number of references has increased # Otherwise raise an error old = len(value.get_references()) value.assembleRefs(self._base) if old == len(value.get_references()): raise InterpolationError('Bad reference count, path:' + repr(path))
def _parse_expression(self, expr): parser = parser_funcs.get_expression_parser() try: tokens = parser.parseString(expr).asList() except pp.ParseException as e: raise ParseError(e.msg, e.line, e.col, e.lineno) if len(tokens) == 2: # options are set passed_opts = [x[1] for x in tokens.pop(0)] self.ignore_failed_render = parser_funcs.IGNORE_ERRORS in passed_opts self.needs_all_envs = parser_funcs.ALL_ENVS in passed_opts elif len(tokens) > 2: raise ExpressionError('Failed to parse %s' % str(tokens), tbFlag=False) self._expr_type = tokens[0][0] self._expr = list(tokens[0][1]) if self._expr_type == parser_funcs.VALUE: self._value_path = DictPath(self._settings.delimiter, self._expr[0][1]).drop_first() self._question = LogicTest([], self._settings.delimiter) self.refs = [] self.inv_refs = [self._value_path] elif self._expr_type == parser_funcs.TEST: self._value_path = DictPath(self._settings.delimiter, self._expr[0][1]).drop_first() self._question = LogicTest(self._expr[2:], self._settings.delimiter) self.refs = self._question.refs self.inv_refs = self._question.inv_refs self.inv_refs.append(self._value_path) elif self._expr_type == parser_funcs.LIST_TEST: self._value_path = None self._question = LogicTest(self._expr[1:], self._settings.delimiter) self.refs = self._question.refs self.inv_refs = self._question.inv_refs else: msg = 'Unknown expression type: %s' raise ExpressionError(msg % self._expr_type, tbFlag=False)
def _parse_expression(self, expr): try: tokens = InvItem._parser.parseString(expr).asList() except pp.ParseException as e: raise ParseError(e.msg, e.line, e.col, e.lineno) if len(tokens) == 1: self._expr_type = tokens[0][0] self._expr = list(tokens[0][1]) elif len(tokens) == 2: for opt in tokens[0]: if opt[1] == _IGNORE_ERRORS: self._ignore_failed_render = True elif opt[1] == _ALL_ENVS: self._needs_all_envs = True self._expr_type = tokens[1][0] self._expr = list(tokens[1][1]) else: raise ExpressionError('Failed to parse %s' % str(tokens), tbFlag=False) if self._expr_type == _VALUE: self._value_path = DictPath(self._settings.delimiter, self._expr[0][1]).drop_first() self._question = Question([], self._settings.delimiter) self._refs = [] self._inv_refs = [ self._value_path ] elif self._expr_type == _TEST: self._value_path = DictPath(self._settings.delimiter, self._expr[0][1]).drop_first() self._question = Question(self._expr[2:], self._settings.delimiter) self._refs = self._question.refs() self._inv_refs = self._question.inv_refs() self._inv_refs.append(self._value_path) elif self._expr_type == _LIST_TEST: self._value_path = None self._question = Question(self._expr[1:], self._settings.delimiter) self._refs = self._question.refs() self._inv_refs = self._question.inv_refs() else: raise ExpressionError('Unknown expression type: %s' % self._expr_type, tbFlag=False)
def _test_expression(self, context, inventory): export_path = None parameter_path = None parameter_value = None test = None value_path = DictPath(self._delimiter, self._expr[0][1]) if self._expr[3][1] == _EQUAL: test = _EQUAL elif self._expr[3][1] == _NOT_EQUAL: test = _NOT_EQUAL export_path, parameter_path, parameter_value = self._get_vars(self._expr[2][1], export_path, parameter_path, parameter_value) export_path, parameter_path, parameter_value = self._get_vars(self._expr[4][1], export_path, parameter_path, parameter_value) if parameter_path is not None: parameter_path.drop_first() parameter_value = self._resolve(parameter_path, context) if export_path is None or parameter_value is None or test is None or value_path is None: ExpressionError('Failed to render %s' % str(self)) export_path.drop_first() value_path.drop_first() results = {} for node, items in inventory.iteritems(): if export_path.exists_in(items): export_value = self._resolve(export_path, items) test_passed = False if test == _EQUAL and export_value == parameter_value: test_passed = True elif test == _NOT_EQUAL and export_value != parameter_value: test_passed = True if test_passed: results[node] = copy.deepcopy(self._resolve(value_path, items)) return results
def _wrap_value(self, value): if isinstance(value, (Value, ValueList)): return value elif isinstance(value, dict): return self._wrap_dict(value) elif isinstance(value, list): return self._wrap_list(value) else: try: return Value(value, self._settings, self._uri, parse_string=self._parse_strings) except InterpolationError as e: e.context = DictPath(self._settings.delimiter) raise
def _get_vars(self, var, export=None, parameter=None, value=None): if isinstance(var, string_types): path = DictPath(self._delimiter, var) if path.path[0].lower() == 'exports': export = path elif path.path[0].lower() == 'self': parameter = path elif path.path[0].lower() == 'true': value = True elif path.path[0].lower() == 'false': value = False else: value = var else: value = var return export, parameter, value
def _merge_recurse(self, cur, new, path=None): if path is None: path = DictPath(self.delimiter) if isinstance(new, dict): if cur is None: cur = {} return self._merge_dict(cur, new, path) elif isinstance(new, list): if cur is None: cur = [] return self._extend_list(cur, new, path) else: return self._update_scalar(cur, new, path)
def _interpolate_inner(self, path, refvalue): self._occurrences[path] = True # mark as seen for ref in refvalue.get_references(): path_from_ref = DictPath(self.delimiter, ref) try: refvalue_inner = self._occurrences[path_from_ref] # If there is no reference, then this will throw a KeyError, # look further down where this is caught and execution passed # to the next iteration of the loop # # If we get here, then the ref references another parameter, # requiring us to recurse, dereferencing first those refs that # are most used and are thus at the leaves of the dependency # tree. if refvalue_inner is True: # every call to _interpolate_inner replaces the value of # the saved occurrences of a reference with True. # Therefore, if we encounter True instead of a refvalue, # it means that we have already processed it and are now # faced with a cyclical reference. raise InfiniteRecursionError(path, ref) self._interpolate_inner(path_from_ref, refvalue_inner) except KeyError as e: # not actually an error, but we are done resolving all # dependencies of the current ref, so move on continue try: new = refvalue.render(self._base) path.set_value(self._base, new) # finally, remove the reference from the occurrences cache del self._occurrences[path] except UndefinedVariableError as e: raise UndefinedVariableError(e.var, path)
def test_inequality_delimiter(self): l = ['a', 'b', 'c'] p1 = DictPath(':', l) p2 = DictPath('%', l) self.assertNotEqual(p1, p2)
def test_new_subpath(self): l = ["a", "b", "c"] p = DictPath(":", l[:-1]) p = p.new_subpath(l[-1]) self.assertListEqual(p.path, l)
def test_get_value_listindex_str(self): v = 42 s = 'a:1:c' d = {'a':[None, {'c':v}, None]} p = DictPath(':', s) self.assertEqual(p.get_value(d), v)
def test_constructor_invalid_type(self): with self.assertRaises(TypeError): p = DictPath(':', 5)
def test_get_value_listindex_str(self): v = 42 s = "a:1:c" d = {"a": [None, {"c": v}, None]} p = DictPath(":", s) self.assertEqual(p.get_value(d), v)
def test_get_value_escaped(self): v = 42 l = ['a', 'b:b', 'c'] d = {'a': {'b:b': {'c': v}}} p = DictPath(':', l) self.assertEqual(p.get_value(d), v)
def test_path_accessor(self): l = ['a', 'b', 'c'] p = DictPath(':', l) self.assertListEqual(p.path, l)
def test_get_value_listindex_list(self): v = 42 l = ["a", 1, "c"] d = {"a": [None, {"c": v}, None]} p = DictPath(":", l) self.assertEqual(p.get_value(d), v)
def resolve(self, context, *args, **kwargs): path = DictPath(kwargs['delim'], self.string) try: return path.get_value(context) except KeyError as e: raise UndefinedVariableError(self.string)
def test_set_nonexistent_value(self): l = ["a", "d"] p = DictPath(":", l) with self.assertRaises(KeyError): p.set_value(dict(), 42)
def test_get_value_listindex_list(self): v = 42 l = ['a', 1, 'c'] d = {'a':[None, {'c':v}, None]} p = DictPath(':', l) self.assertEqual(p.get_value(d), v)
def test_get_value_escaped(self): v = 42 l = ['a', 'b:b', 'c'] d = {'a':{'b:b':{'c':v}}} p = DictPath(':', l) self.assertEqual(p.get_value(d), v)
def test_repr(self): delim = '%' s = 'a:b\:b:c' p = DictPath(delim, s) self.assertEqual('%r' % p, 'DictPath(%r, %r)' % (delim, s))
def test_str(self): s = 'a:b\:b:c' p = DictPath(':', s) self.assertEqual(str(p), s)
def test_set_nonexistent_value(self): l = ['a', 'd'] p = DictPath(':', l) with self.assertRaises(KeyError): p.set_value(dict(), 42)
def test_new_subpath(self): l = ['a', 'b', 'c'] p = DictPath(':', l[:-1]) p = p.new_subpath(l[-1]) self.assertListEqual(p.path, l)
def test_constructor_list(self): l = ['a', 'b', 'c'] p = DictPath(':', l) self.assertListEqual(p._parts, l)
def test_get_value_listindex_list(self): v = 42 l = ['a', 1, 'c'] d = {'a': [None, {'c': v}, None]} p = DictPath(':', l) self.assertEqual(p.get_value(d), v)
def test_constructor_str_escaped(self): delim = ':' s = 'a{0}b\{0}b{0}c'.format(delim) l = ['a', 'b\\{0}b'.format(delim), 'c'] p = DictPath(delim, s) self.assertListEqual(p._parts, l)
def test_get_value_escaped(self): v = 42 l = ["a", "b:b", "c"] d = {"a": {"b:b": {"c": v}}} p = DictPath(":", l) self.assertEqual(p.get_value(d), v)
def _resolve(self, ref, context): path = DictPath(self._delim, ref) try: return path.get_value(context) except KeyError as e: raise UndefinedVariableError(ref)