Ejemplo n.º 1
0
    def __init__(
            self,
            name,
            namespace=None,
            types=tuple(),
            messages=tuple(),
    ):
        """Initializes a new protocol object.

    Args:
      name: Protocol name (absolute or relative).
      namespace: Optional explicit namespace (if name is relative).
      types: Collection of types in the protocol.
      messages: Collection of messages in the protocol.
    """
        self._avro_name = schema.Name(name=name, namespace=namespace)
        self._fullname = self._avro_name.fullname
        self._name = self._avro_name.simple_name
        self._namespace = self._avro_name.namespace

        self._props = {}
        self._props['name'] = self._name
        if self._namespace:
            self._props['namespace'] = self._namespace

        self._names = schema.Names(default_namespace=self._namespace)

        self._types = tuple(types)
        # Map: type full name -> type schema
        self._type_map = MappingProxyType(
            {type.fullname: type
             for type in self._types})
        # This assertion cannot fail unless we don't track named schemas properly:
        assert (len(self._types) == len(
            self._type_map)), ('Type list %r does not match type map: %r' %
                               (self._types, self._type_map))
        # TODO: set props['types']

        self._messages = tuple(messages)

        # Map: message name -> Message
        # Note that message names are simple names unique within the protocol.
        self._message_map = MappingProxyType(
            {message.name: message
             for message in self._messages})
        if len(self._messages) != len(self._message_map):
            raise ProtocolParseException(
                'Invalid protocol %s with duplicate message name: %r' %
                (self._avro_name, self._messages))
        # TODO: set props['messages']

        self._md5 = hashlib.md5(str(self).encode('utf-8')).digest()
Ejemplo n.º 2
0
    def test_fullname(self):
        """The fullname is determined in one of the following ways:
     * A name and namespace are both specified.  For example,
       one might use "name": "X", "namespace": "org.foo"
       to indicate the fullname "org.foo.X".
     * A fullname is specified.  If the name specified contains
       a dot, then it is assumed to be a fullname, and any
       namespace also specified is ignored.  For example,
       use "name": "org.foo.X" to indicate the
       fullname "org.foo.X".
     * A name only is specified, i.e., a name that contains no
       dots.  In this case the namespace is taken from the most
       tightly encosing schema or protocol.  For example,
       if "name": "X" is specified, and this occurs
       within a field of the record definition
       of "org.foo.Y", then the fullname is "org.foo.X".

        References to previously defined names are as in the latter
        two cases above: if they contain a dot they are a fullname, if
        they do not contain a dot, the namespace is the namespace of
        the enclosing definition.

        Primitive type names have no namespace and their names may
        not be defined in any namespace.  A schema may only contain
        multiple definitions of a fullname if the definitions are
        equivalent.
        """
        # relative name and namespace specified
        self.assertEqual(schema.Name('a', 'o.a.h').fullname, 'o.a.h.a')

        # absolute name and namespace specified
        self.assertEqual(schema.Name('.a', 'o.a.h').fullname, '.a')

        # absolute name and namespace specified
        fullname = schema.Name('a.b.c.d', 'o.a.h').fullname
        self.assertEqual(fullname, 'a.b.c.d')
Ejemplo n.º 3
0
def ProtocolFromJSONData(json_data):
    """Builds an Avro  Protocol from its JSON descriptor.

  Args:
    json_data: JSON data representing the descriptor of the Avro protocol.
  Returns:
    The Avro Protocol parsed from the JSON descriptor.
  Raises:
    ProtocolParseException: if the descriptor is invalid.
  """
    if type(json_data) != dict:
        print(type(json_data))
        raise ProtocolParseException(
            'Invalid JSON descriptor for an Avro protocol: %r' % json_data)

    name = json_data.get('protocol')
    if name is None:
        raise ProtocolParseException(
            'Invalid protocol descriptor with no "name": %r' % json_data)

    # Namespace is optional
    namespace = json_data.get('namespace')

    avro_name = schema.Name(name=name, namespace=namespace)
    names = schema.Names(default_namespace=avro_name.namespace)

    type_desc_list = json_data.get('types', tuple())
    types = tuple(
        map(lambda desc: Protocol._ParseTypeDesc(desc, names=names),
            type_desc_list))

    message_desc_map = json_data.get('messages', dict())
    messages = tuple(
        Protocol._ParseMessageDescMap(message_desc_map, names=names))

    return Protocol(
        name=name,
        namespace=namespace,
        types=types,
        messages=messages,
    )
Ejemplo n.º 4
0
class Protocol(object):
    """An application protocol."""
    def _parse_types(self, types, type_names):
        type_objects = []
        for type in types:
            type_object = schema.make_avsc_object(type, type_names)
            if type_object.type not in VALID_TYPE_SCHEMA_TYPES:
                fail_msg = 'Type %s not an enum, fixed, record, or error.' % type
                raise ProtocolParseException(fail_msg)
            type_objects.append(type_object)
        return type_objects

    def _parse_messages(self, messages, names):
        message_objects = {}
        for name, body in messages.items():
            if name in message_objects:
                fail_msg = 'Message name "%s" repeated.' % name
                raise ProtocolParseException(fail_msg)
            elif not (hasattr(body, 'get') and callable(body.get)):
                fail_msg = 'Message name "%s" has non-object body %s.' % (name,
                                                                          body)
                raise ProtocolParseException(fail_msg)
            request = body.get('request')
            response = body.get('response')
            errors = body.get('errors')
            message_objects[name] = Message(name, request, response, errors,
                                            names)
        return message_objects

    def __init__(self, name, namespace=None, types=None, messages=None):
        # Ensure valid ctor args
        if not name:
            fail_msg = 'Protocols must have a non-empty name.'
            raise ProtocolParseException(fail_msg)
        elif not isinstance(name, six.string_types):
            fail_msg = 'The name property must be a string.'
            raise ProtocolParseException(fail_msg)
        elif namespace is not None and not isinstance(namespace,
                                                      six.string_types):
            fail_msg = 'The namespace property must be a string.'
            raise ProtocolParseException(fail_msg)
        elif types is not None and not isinstance(types, list):
            fail_msg = 'The types property must be a list.'
            raise ProtocolParseException(fail_msg)
        elif (messages is not None
              and not (hasattr(messages, 'get') and callable(messages.get))):
            fail_msg = 'The messages property must be a JSON object.'
            raise ProtocolParseException(fail_msg)

        self._props = {}
        self.set_prop('name', name)
        type_names = schema.Names()
        if namespace is not None:
            self.set_prop('namespace', namespace)
            type_names.default_namespace = namespace
        if types is not None:
            self.set_prop('types', self._parse_types(types, type_names))
        if messages is not None:
            self.set_prop('messages',
                          self._parse_messages(messages, type_names))
        self._md5 = md5(str(self).encode('US-ASCII')).digest()

    # read-only properties
    name = property(lambda self: self.get_prop('name'))
    namespace = property(lambda self: self.get_prop('namespace'))
    fullname = property(
        lambda self: schema.Name(self.name, self.namespace).fullname)
    types = property(lambda self: self.get_prop('types'))
    types_dict = property(
        lambda self: dict([(type.name, type) for type in self.types]))
    messages = property(lambda self: self.get_prop('messages'))
    md5 = property(lambda self: self._md5)
    props = property(lambda self: self._props)

    # utility functions to manipulate properties dict
    def get_prop(self, key):
        return self.props.get(key)

    def set_prop(self, key, value):
        self.props[key] = value

    def to_json(self):
        to_dump = {}
        to_dump['protocol'] = self.name
        names = schema.Names(default_namespace=self.namespace)
        if self.namespace:
            to_dump['namespace'] = self.namespace
        if self.types:
            to_dump['types'] = [t.to_json(names) for t in self.types]
        if self.messages:
            messages_dict = {}
            for name, body in self.messages.items():
                messages_dict[name] = body.to_json(names)
            to_dump['messages'] = messages_dict
        return to_dump

    def __str__(self):
        return json.dumps(self.to_json(), sort_keys=True)

    def __eq__(self, that):
        to_cmp = json.loads(str(self))
        return to_cmp == json.loads(str(that))
Ejemplo n.º 5
0
 def test_null_namespace(self):
     """The empty string may be used as a namespace to indicate the null namespace."""
     name = schema.Name('name', "", None)
     self.assertEqual(name.fullname, "name")
     self.assertIsNone(name.space)
Ejemplo n.º 6
0
 def test_equal_names(self):
     """Equality of names is defined on the fullname and is case-sensitive."""
     self.assertEqual(schema.Name('a.b.c.d', None, None),
                      schema.Name('d', 'a.b.c', None))
     self.assertNotEqual(schema.Name('C.d', None, None),
                         schema.Name('c.d', None, None))
Ejemplo n.º 7
0
 def test_name_space_default_specified(self):
     """When name and space are specified, default space should be ignored."""
     fullname = schema.Name('a', 'o.a.a', 'o.a.h').fullname
     self.assertEqual(fullname, 'o.a.a.a')
Ejemplo n.º 8
0
 def test_fullname_space_default_specified(self):
     """When a name contains dots, namespace and default space should be ignored."""
     fullname = schema.Name('a.b.c.d', 'o.a.a', 'o.a.h').fullname
     self.assertEqual(fullname, 'a.b.c.d')
Ejemplo n.º 9
0
 def test_name_default_specified(self):
     """Default space becomes the namespace when the namespace is None."""
     fullname = schema.Name('a', None, 'b.c.d').fullname
     self.assertEqual(fullname, 'b.c.d.a')
Ejemplo n.º 10
0
 def test_name_space_specified(self):
     """Space combines with a name to become the fullname."""
     # name and namespace specified
     fullname = schema.Name('a', 'o.a.h', None).fullname
     self.assertEqual(fullname, 'o.a.h.a')
Ejemplo n.º 11
0
 def test_name_is_none(self):
     """When a name is None its namespace is None."""
     self.assertIsNone(schema.Name(None, None, None).fullname)
     self.assertIsNone(schema.Name(None, None, None).space)