Exemplo n.º 1
0
def sprint(obj, indent=4, bullet='* '):
    '''
    Display JSON-like objects in a compact way. Differently to pretty print, it
    does not format objects using a valid dict/list syntax.
    
    Examples
    --------
    
    >>> obj = {'foo': {'foobar': 1, 'bar': 'null'}, 'bar': ['null', 1, 2, 3]} 
    >>> print(sprint(obj))
    bar:
        * null
        * 1
        * 2
        * 3
    foo:
        bar: null
        foobar: 1
    '''
    if is_object(obj):
        if obj:
            items = []
            obj_items = obj.items()
            obj_items.sort()
            for key, value in obj_items:
                if is_object(value) or is_array(value):
                    value = '\n' + _indent_f(sprint(value), indent)
                else:
                    value = ' ' + sprint(value)
                items.append('%s:%s' % (key, value))
            return '\n'.join(items)
        return '<empty>'
    elif is_array(obj):
        if obj:
            return itemize(map(sprint, obj))
        else:
            return '<empty>'
    else:
        data = unicode(obj)
        if '\n' in data:
            data = '\n' + _indent_f(data, indent)

        return data
Exemplo n.º 2
0
def sprint(obj, indent=4, bullet='* '):
    '''
    Display JSON-like objects in a compact way. Differently to pretty print, it
    does not format objects using a valid dict/list syntax.
    
    Examples
    --------
    
    >>> obj = {'foo': {'foobar': 1, 'bar': 'null'}, 'bar': ['null', 1, 2, 3]} 
    >>> print(sprint(obj))
    bar:
        * null
        * 1
        * 2
        * 3
    foo:
        bar: null
        foobar: 1
    '''
    if is_object(obj):
        if obj:
            items = []
            obj_items = obj.items()
            obj_items.sort()
            for key, value in obj_items:
                if is_object(value) or is_array(value):
                    value = '\n' + _indent_f(sprint(value), indent)
                else:
                    value = ' ' + sprint(value)
                items.append('%s:%s' % (key, value))
            return '\n'.join(items)
        return '<empty>'
    elif is_array(obj):
        if obj:
            return itemize(map(sprint, obj))
        else:
            return '<empty>'
    else:
        data = unicode(obj)
        if '\n' in data:
            data = '\n' + _indent_f(data, indent)

        return data
Exemplo n.º 3
0
    def validate(self, obj, lazy=True, path=[]):
        # Check if obj is a dict
        if not pyson_t.is_object(obj):
            msg = "object of type '%s' is not a mapping." % type(obj).__name__
            raise errors.ValidationError((msg, path))

        # Validate root
        obj_keys = set(obj)
        valid_root = False
        if self.root_key is not None:
            try:
                root = obj[self.root_key]
            except KeyError:
                raise errors.MissingKeyError((None, path + [self.root_key]))
            else:
                self._root_schema.validate(root, lazy, path)
                obj_keys.remove(self.root_key)
                valid_root = True

        # Catch errors and re-raise exception as PartialValidation if
        # valid_root is True
        try:
            # Validate all keys
            for key, validator in self._schemas.items():
                # Get value from object and check if key exists
                try:
                    value = obj[key]
                    obj_keys.remove(key)
                except KeyError:
                    if not validator.can_be_empty(lazy):
                        raise errors.MissingKeyError((None, path + [key]))
                else:
                    # Validate value
                    validator.validate(value, lazy, path + [key])

            # Assert obj do not have extra keys
            if obj_keys:
                first_key = iter(obj_keys).next()
                raise errors.InvalidKeyError((None, path + [first_key]))
        except Exception as ex:
            if valid_root:
                raise errors.PartialValidationError(ex)
            else:
                raise ex
Exemplo n.º 4
0
    def adapt(self, obj, inplace=False, path=[]):
        # Check if obj is a dict
        if not pyson_t.is_object(obj):
            raise errors.AdaptationError(('%(path)s: not a mapping', path))

        new_obj = obj.copy()

        # Test the root key
        root_value = None
        if self.root_key is not None:
            try:
                root_value = new_obj.pop(self.root_key)
            except KeyError:
                raise errors.AdaptationError((None, path))
            else:
                root_value = self._root_schema.adapt(root_value, inplace, path)

        # Catches AdaptationError and raises PartialAdaptationError if root
        # exists
        try:
            for k, v in new_obj.items():
                try:
                    adapter = self[k].adapt
                except KeyError:
                    raise errors.AdaptationError((None, path))
                else:
                    new_obj[k] = adapter(v, inplace, path + [k])
        except errors.AdaptationError as ex:
            if self.root_key is not None:
                raise errors.PartialAdaptationError(ex.args[0], ex)

        # Writes root key back to the dictionary
        if self.root_key is not None:
            new_obj[self.root_key] = root_value

        # Checks validity
        if self.is_valid(new_obj):
            if inplace:
                obj.update(new_obj)
                return obj
            else:
                return new_obj
Exemplo n.º 5
0
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
Exemplo n.º 6
0
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
Exemplo n.º 7
0
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
Exemplo n.º 8
0
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