def updateitem(obj, path, value): ''' Updates ``obj``'s ``path`` node to given ``value``. Raises ------ IndexKeyError If the node is empty. ''' path = as_path(path) if haspath(obj, path): obj = getitem(obj, path[:-1]) obj[path[-1]] = value else: raise IndexKeyError('%s is empty' % as_str_path(path))
def setitem(obj, path, value, newmap=None): ''' Updates ``obj``'s ``path`` node to given ``value``. Recursively creates and updates new keys for mapping containers. Parameters ---------- obj : JSON-like JSON-like structure. path : str or list path Any valid JSON path. value : object Any value to be assigned to the given path node. newmap : callable Factory function for creating new mappings. By default it tries to use the same type as the innermost dictionary. This function, called with no arguments, should return a new dictionary-like object. Raises ------ IndexKeyError If a node in a sequence container is empty. ''' path = as_path(path) curr_obj = obj # Find the first empty node idx = 0 objs = [obj] for node in path[:-1]: try: curr_obj = curr_obj[node] objs.append(curr_obj) idx += 1 except KeyError: break_node = node break_obj = curr_obj break except IndexError: path = as_str_path(path[:idx + 1]) raise IndexKeyError('empty node %s in sequence.' % path) else: # There is no empty node: assign value try: curr_obj[path[-1]] = value return except IndexError: raise IndexKeyError('empty node %s in sequence.' % as_str_path(path)) # An empty node was found: inspect the missing structures empty_path = path[idx:] # Asserts that no integer index exists for the new nodes: it only fills # new dictionaries empty_tt = map(type, empty_path) if int in empty_tt: raise IndexKeyError('attempt to create sequence') # Obtain the factory function for creating new mappings if newmap is None: objs.reverse() for obj in objs: if is_object(obj): newmap = type(obj) break else: newmap = dict # Fill dictionary values curr = empty = newmap() for node in empty_path[1:-1]: curr[node] = newmap() curr = curr[node] curr[empty_path[-1]] = value # Commit new dictionary to 'obj' break_obj[break_node] = empty
def writeitem(obj, path, value, newmap=None, newseq=None, newitem=None): ''' In most cases, it behaves like the __setitem__ iterface: setitem(obj, key, value) <==> obj[key] = value. The two optional arguments 'fill' and 'fill_value' defines how list-like sequences are handled if 'key' is an invalid index. If 'fill' is True (default) and key == len(obj), thus indices are [0, 1, ..., len(obj) - 1], 'value' is appended to the end of the list. This behavior creates the new entry that is equivalent to 'obj[key] == value'. If 'fill' is True and key > len(obj), the function checks if the user had defined the 'fill_value' argument. The list is then filled with this value until the obj[key] is reached, and finally value is appended to the list. ''' #TODO: support completion in array objects raise NotImplementedError path = as_path(path) curr_obj = obj # Find the first empty node idx = 0 objs = [obj] for node in path[:-1]: try: curr_obj = curr_obj[node] objs.append(curr_obj) idx += 1 except KeyError: break_node = node break_obj = curr_obj break except IndexError: path = as_str_path(path[:idx + 1]) raise IndexKeyError('empty node %s in sequence.' % path) else: # There is no empty node: assign value try: curr_obj[path[-1]] = value return except IndexError: raise IndexKeyError('empty node %s in sequence.' % as_str_path(path)) # An empty node was found: inspect the missing structures empty_path = path[idx:] # Asserts that no integer index exists for the new nodes: it only fills # new dictionaries empty_tt = map(type, empty_path) if int in empty_tt: raise IndexKeyError('attempt to create sequence') # Obtain the factory function for creating new mappings if newmap is None: objs.reverse() for obj in objs: if is_object(obj): newmap = type(obj) break else: newmap = dict # Fill dictionary values curr = empty = newmap() for node in empty_path[1:-1]: curr[node] = newmap() curr = curr[node] curr[empty_path[-1]] = value # Commit new dictionary to 'obj' break_obj[break_node] = empty