Esempio n. 1
0
def _decode_union(data_type, obj, alias_validators, strict, for_msgpack):
    """
    The data_type argument must be a Union.
    See json_compat_obj_decode() for argument descriptions.
    """
    val = None
    if isinstance(obj, six.string_types):
        # Handles the shorthand format where the union is serialized as only
        # the string of the tag.
        tag = obj
        if tag in data_type.definition._tagmap:
            val_data_type = data_type.definition._tagmap[tag]
            if not isinstance(val_data_type, (bv.Void, bv.Nullable)):
                raise bv.ValidationError(
                    "expected object for '%s', got symbol" % tag)
            if tag == data_type.definition._catch_all:
                raise bv.ValidationError(
                    "unexpected use of the catch-all tag '%s'" % tag)
        else:
            if not strict and data_type.definition._catch_all:
                tag = data_type.definition._catch_all
            else:
                raise bv.ValidationError("unknown tag '%s'" % tag)
    elif isinstance(obj, dict):
        tag, val = _decode_union_dict(
            data_type, obj, alias_validators, strict, for_msgpack)
    else:
        raise bv.ValidationError("expected string or object, got %s" %
                                 bv.generic_type_name(obj))
    return data_type.definition(tag, val)
Esempio n. 2
0
def _determine_struct_tree_subtype(data_type, obj, strict):
    """
    Searches through the JSON-object-compatible dict using the data type
    definition to determine which of the enumerated subtypes `obj` is.
    """
    if '.tag' not in obj:
        raise bv.ValidationError("missing '.tag' key")
    if not isinstance(obj['.tag'], six.string_types):
        raise bv.ValidationError('expected string, got %s' %
                                 bv.generic_type_name(obj['.tag']),
                                 parent='.tag')

    # Find the subtype the tags refer to
    full_tags_tuple = (obj['.tag'],)
    if full_tags_tuple in data_type.definition._tag_to_subtype_:
        subtype = data_type.definition._tag_to_subtype_[full_tags_tuple]
        if isinstance(subtype, bv.StructTree):
            raise bv.ValidationError("tag '%s' refers to non-leaf subtype" %
                                     ('.'.join(full_tags_tuple)))
        return subtype
    else:
        if strict:
            # In strict mode, the entirety of the tag hierarchy should
            # point to a known subtype.
            raise bv.ValidationError("unknown subtype '%s'" %
                                     '.'.join(full_tags_tuple))
        else:
            # If subtype was not found, use the base.
            if data_type.definition._is_catch_all_:
                return data_type
            else:
                raise bv.ValidationError(
                    "unknown subtype '%s' and '%s' is not a catch-all" %
                    ('.'.join(full_tags_tuple), data_type.definition.__name__))
Esempio n. 3
0
def _encode_union_old(data_type, obj, alias_validators, for_msgpack):
    """
    The data_type argument must be a Union.
    See json_encode() for argument descriptions.
    """
    if obj._tag is None:
        raise bv.ValidationError('no tag set')
    field_data_type = data_type.definition._tagmap[obj._tag]
    if field_data_type is None:
        return obj._tag
    else:
        if (isinstance(field_data_type, bv.Void) or
                (isinstance(field_data_type, bv.Nullable) and
                 obj._value is None)):
            return obj._tag
        else:
            try:
                encoded_val = _json_compat_obj_encode_helper(
                    field_data_type, obj._value, alias_validators, True,
                    for_msgpack)
            except bv.ValidationError as e:
                e.add_parent(obj._tag)
                raise
            else:
                return {obj._tag: encoded_val}
Esempio n. 4
0
def _encode_struct(data_type, obj, alias_validators, old_style, for_msgpack):
    """
    The data_type argument must be a Struct or StructTree.
    See json_encode() for argument descriptions.
    """
    # We skip validation of fields with primitive data types in structs and
    # unions because they've already been validated on assignment.
    d = collections.OrderedDict()
    for field_name, field_data_type in data_type.definition._all_fields_:
        try:
            val = getattr(obj, field_name)
        except AttributeError as e:
            raise bv.ValidationError(e.args[0])
        presence_key = '_%s_present' % field_name
        if val is not None and getattr(obj, presence_key):
            # This check makes sure that we don't serialize absent struct
            # fields as null, even if there is a default.
            try:
                d[field_name] = _json_compat_obj_encode_helper(
                    field_data_type, val, alias_validators, old_style,
                    for_msgpack)
            except bv.ValidationError as e:
                e.add_parent(field_name)
                raise
    return d
Esempio n. 5
0
def _encode_union(data_type, obj, alias_validators, for_msgpack):
    """
    The data_type argument must be a Union.
    See json_encode() for argument descriptions.
    """
    if obj._tag is None:
        raise bv.ValidationError('no tag set')
    field_data_type = data_type.definition._tagmap[obj._tag]

    if (isinstance(field_data_type, bv.Void) or
            (isinstance(field_data_type, bv.Nullable) and obj._value is None)):
        return {'.tag': obj._tag}
    else:
        try:
            encoded_val = _json_compat_obj_encode_helper(
                field_data_type, obj._value, alias_validators, False,
                for_msgpack)
        except bv.ValidationError as e:
            e.add_parent(obj._tag)
            raise
        else:
            if isinstance(field_data_type, bv.Nullable):
                # We've already checked for the null case above, so now we're
                # only interested in what the wrapped validator is.
                field_data_type = field_data_type.validator
            if (isinstance(field_data_type, bv.Struct) and
                    not isinstance(field_data_type, bv.StructTree)):
                d = collections.OrderedDict()
                d['.tag'] = obj._tag
                d.update(encoded_val)
                return d
            else:
                return collections.OrderedDict([
                    ('.tag', obj._tag),
                    (obj._tag, encoded_val)])
def json_decode(data_type, serialized_obj, strict=True, old_style=False):
    """Performs the reverse operation of json_encode.

    Args:
        data_type (Validator): Validator for serialized_obj.
        serialized_obj (str): The JSON string to deserialize.
        strict (bool): If strict, then unknown struct fields will raise an
            error, and unknown union variants will raise an error even if a
            catch all field is specified. strict should only be used by a
            recipient of serialized JSON if it's guaranteed that its Babel
            specs are at least as recent as the senders it receives messages
            from.

    Returns:
        The returned object depends on the input data_type.
            - Binary -> bytes
            - Boolean -> bool
            - Float -> float
            - Integer -> long
            - List -> list
            - Nullable -> None or its wrapped type.
            - String -> unicode (PY2) or str (PY3)
            - Struct -> An instance of its definition attribute.
            - Timestamp -> datetime.datetime
            - Union -> An instance of its definition attribute.
    """
    try:
        deserialized_obj = json.loads(serialized_obj)
    except ValueError:
        raise bv.ValidationError('could not decode input as JSON')
    else:
        return json_compat_obj_decode(data_type, deserialized_obj, strict, old_style)
def _decode_struct(data_type, obj, strict, old_style):
    """
    The data_type argument must be a Struct.
    See json_compat_obj_decode() for argument descriptions.
    """
    if not isinstance(obj, dict):
        raise bv.ValidationError('expected object, got %s' %
                                 bv.generic_type_name(obj))
    if strict:
        for key in obj:
            if (key not in data_type.definition._all_field_names_ and
                    not key.startswith('.tag')):
                raise bv.ValidationError("unknown field '%s'" % key)
    ins = data_type.definition()
    _decode_struct_fields(ins, data_type.definition._all_fields_, obj, strict,
                          old_style)
    # Check that all required fields have been set.
    data_type.validate_fields_only(ins)
    return ins
def _decode_list(data_type, obj, strict, old_style):
    """
    The data_type argument must be a List.
    See json_compat_obj_decode() for argument descriptions.
    """
    if not isinstance(obj, list):
        raise bv.ValidationError(
            'expected list, got %s' % bv.generic_type_name(obj))
    return [_json_compat_obj_decode_helper(data_type.item_validator,
                                           item, strict, old_style)
            for item in obj]
def _make_babel_friendly(data_type, val, strict, validate):
    """
    Convert a Python object to a type that will pass validation by its
    validator.
    """
    if isinstance(data_type, bv.Timestamp):
        try:
            return datetime.datetime.strptime(val, data_type.format)
        except ValueError as e:
            raise bv.ValidationError(e.args[0])
    elif isinstance(data_type, bv.Binary):
        try:
            return base64.b64decode(val)
        except TypeError:
            raise bv.ValidationError('invalid base64-encoded binary')
    elif isinstance(data_type, bv.Void):
        if strict and val is not None:
            raise bv.ValidationError("expected null, got value")
        return None
    else:
        if validate:
            data_type.validate(val)
        return val
def _decode_union_old(data_type, obj, strict):
    """
    The data_type argument must be a Union.
    See json_compat_obj_decode() for argument descriptions.
    """
    val = None
    if isinstance(obj, six.string_types):
        # Union member has no associated value
        tag = obj
        if tag in data_type.definition._tagmap:
            val_data_type = data_type.definition._tagmap[tag]
            if not isinstance(val_data_type, (bv.Void, bv.Nullable)):
                raise bv.ValidationError(
                    "expected object for '%s', got symbol" % tag)
        else:
            if not strict and data_type.definition._catch_all:
                tag = data_type.definition._catch_all
            else:
                raise bv.ValidationError("unknown tag '%s'" % tag)
    elif isinstance(obj, dict):
        # Union member has value
        if len(obj) != 1:
            raise bv.ValidationError('expected 1 key, got %s' % len(obj))
        tag = list(obj)[0]
        raw_val = obj[tag]
        if tag in data_type.definition._tagmap:
            val_data_type = data_type.definition._tagmap[tag]
            if isinstance(val_data_type, bv.Nullable) and raw_val is None:
                val = None
            elif isinstance(val_data_type, bv.Void):
                if raw_val is None or not strict:
                    # If raw_val is None, then this is the more verbose
                    # representation of a void union member. If raw_val isn't
                    # None, then maybe the spec has changed, so check if we're
                    # in strict mode.
                    val = None
                else:
                    raise bv.ValidationError('expected null, got %s' %
                                             bv.generic_type_name(raw_val))
            else:
                try:
                    val = _json_compat_obj_decode_helper(val_data_type,
                                                         raw_val,
                                                         strict,
                                                         old_style=True)
                except bv.ValidationError as e:
                    e.add_parent(tag)
                    raise
        else:
            if not strict and data_type.definition._catch_all:
                tag = data_type.definition._catch_all
            else:
                raise bv.ValidationError("unknown tag '%s'" % tag)
    else:
        raise bv.ValidationError("expected string or object, got %s" %
                                 bv.generic_type_name(obj))
    return data_type.definition(tag, val)
Esempio n. 11
0
def _make_babel_friendly(
        data_type, val, alias_validators, strict, validate, for_msgpack):
    """
    Convert a Python object to a type that will pass validation by its
    validator.

    Validation by ``alias_validators`` is performed even if ``validate`` is
    false.
    """
    if isinstance(data_type, bv.Timestamp):
        try:
            ret = datetime.datetime.strptime(val, data_type.format)
        except ValueError as e:
            raise bv.ValidationError(e.args[0])
    elif isinstance(data_type, bv.Bytes):
        if for_msgpack:
            if isinstance(val, six.text_type):
                ret = val.encode('utf-8')
            else:
                ret = val
        else:
            try:
                ret = base64.b64decode(val)
            except TypeError:
                raise bv.ValidationError('invalid base64-encoded bytes')
    elif isinstance(data_type, bv.Void):
        if strict and val is not None:
            raise bv.ValidationError("expected null, got value")
        return None
    else:
        if validate:
            data_type.validate(val)
        ret = val
    if alias_validators is not None and data_type in alias_validators:
        alias_validators[data_type](ret)
    return ret
Esempio n. 12
0
def _decode_union_dict(data_type, obj, alias_validators, strict, for_msgpack):
    if '.tag' not in obj:
        raise bv.ValidationError("missing '.tag' key")
    tag = obj['.tag']
    if not isinstance(tag, six.string_types):
        raise bv.ValidationError(
            'tag must be string, got %s' % bv.generic_type_name(tag))

    if tag not in data_type.definition._tagmap:
        if not strict and data_type.definition._catch_all:
            return data_type.definition._catch_all, None
        else:
            raise bv.ValidationError("unknown tag '%s'" % tag)
    if tag == data_type.definition._catch_all:
        raise bv.ValidationError(
            "unexpected use of the catch-all tag '%s'" % tag)

    val_data_type = data_type.definition._tagmap[tag]
    if isinstance(val_data_type, bv.Nullable):
        val_data_type = val_data_type.validator
        nullable = True
    else:
        nullable = False

    if isinstance(val_data_type, bv.Void):
        if tag in obj:
            if obj[tag] is not None:
                raise bv.ValidationError('expected null, got %s' %
                                         bv.generic_type_name(obj[tag]))
        for key in obj:
            if key != tag and key != '.tag':
                raise bv.ValidationError("unexpected key '%s'" % key)
        val = None
    elif isinstance(val_data_type,
                    (bv.Primitive, bv.List, bv.StructTree, bv.Union)):
        if tag in obj:
            raw_val = obj[tag]
            try:
                val = _json_compat_obj_decode_helper(
                    val_data_type, raw_val, alias_validators, strict, False, for_msgpack)
            except bv.ValidationError as e:
                e.add_parent(tag)
                raise
        else:
            # Check no other keys
            if nullable:
                val = None
            else:
                raise bv.ValidationError("missing '%s' key" % tag)
        for key in obj:
            if key != tag and key != '.tag':
                raise bv.ValidationError("unexpected key '%s'" % key)
    elif isinstance(val_data_type, bv.Struct):
        if nullable and len(obj) == 1:  # only has a .tag key
            val = None
        else:
            # assume it's not null
            raw_val = obj
            try:
                val = _json_compat_obj_decode_helper(
                    val_data_type, raw_val, alias_validators, strict, False,
                    for_msgpack)
            except bv.ValidationError as e:
                e.add_parent(tag)
                raise
    else:
        assert False, type(val_data_type)
    return tag, val