prkumar/uplink-protobuf

View on GitHub
uplink_protobuf/converter.py

Summary

Maintainability
A
0 mins
Test Coverage
# Static imports
import functools
import inspect

# Third party imports
from google.protobuf import message
from uplink import converters, returns, json

# Local imports
from uplink_protobuf import helpers, json_options


__all__ = ["ProtocolBuffersConverter"]


class ProtocolBuffersConverter(converters.Factory):
    __DECODING_STRATEGIES = {
        returns.json: json_options.parse_json
    }

    __ENCODING_STRATEGIES = {
        json: json_options.convert_message_to_dict
    }

    # === BEGIN Helpers === #

    @staticmethod
    def is_protocol_buffer_class(cls):
        """
        Returns whether or not the given `cls` is a protobuf message
        subclass.
        """
        return inspect.isclass(cls) and issubclass(cls, message.Message)

    @staticmethod
    def _get_strategy(cls, request_definition, strategies):
        if not ProtocolBuffersConverter.is_protocol_buffer_class(cls):
            # Returning None tells Uplink's converter layer that we
            # can't handle this type, so it can move on to the next
            # factory.
            return None

        strategy_keys = tuple(strategies.keys())
        method_annotations = request_definition.method_annotations

        try:
            key = helpers.get_first_of_type(method_annotations, strategy_keys)
        except StopIteration:
            raise KeyError
        else:
            target_strategy = strategies[type(key)]
            return functools.partial(target_strategy, cls, request_definition)

    # === END Helpers === #

    def create_response_body_converter(self, cls, request_definition):
        try:
            return self._get_strategy(
                cls, request_definition, self.__DECODING_STRATEGIES
            )
        except KeyError:
            # Return default callable that can decode Protobuf message
            # from response content.
            def converter(response):
                msg = cls()
                msg.ParseFromString(response.content)
                return msg

            return converter

    def create_request_body_converter(self, cls, request_definition):
        try:
            return self._get_strategy(
                cls, request_definition, self.__ENCODING_STRATEGIES
            )
        except KeyError:
            # Return callable that can serialize Protobuf message.
            def converter(msg):
                return msg.SerializeToString()

            return converter