Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
namara-python / method_generator.py
Size: Mime:
from namara_python.utils import to_camel_case
import namara_python.classes as cl
from namara_python.rpc.organizations.organizations_pb2_twirp import OrganizationsServiceClient
from namara_python.rpc.catalog.catalog_pb2_twirp import CatalogServiceClient
from namara_python.rpc.category.category_pb2_twirp import CategoryServiceClient
from namara_python.rpc.grants.grants_pb2_twirp import GrantsServiceClient
from namara_python.rpc.sources.sources_pb2_twirp import SourcesServiceClient
from namara_python.rpc.references.references_pb2_twirp import ReferencesServiceClient

# must import these so that they are available to the symbol database which
# registers all the rpc objects, which is required before method generation begins
import namara_python.rpc.organizations.organizations_pb2
import namara_python.rpc.catalog.catalog_pb2
import namara_python.rpc.category.category_pb2
import namara_python.rpc.grants.grants_pb2
import namara_python.rpc.sources.sources_pb2
import namara_python.rpc.references.references_pb2

import inspect
import json
from typing import Callable, Any
from google.protobuf.json_format import MessageToJson
from google.protobuf import symbol_database as _symbol_database

_sym_db = _symbol_database.Default()

RPC_CLIENT_TO_LIB_CLS_MAPPING = {
    OrganizationsServiceClient: cl.Organization,
    CatalogServiceClient: cl.Catalog,
    CategoryServiceClient: cl.Category,
    GrantsServiceClient: cl.Grant,
    SourcesServiceClient: cl.Source,
    ReferencesServiceClient: cl.Reference
}

TWIRP_FUNCTION = Callable[[Any, Any], Any]

def serialize_input(twirp_func:TWIRP_FUNCTION) -> TWIRP_FUNCTION:
    def wrapper(self, *args, **kwargs):

        input_obj_name = _get_input_object_name(twirp_func, self)

        serialized_input_obj = _sym_db.GetSymbol(input_obj_name)(**kwargs)

        # here self is an instance of NamaraPython.<class>, which all have
        # `rpc_client` as an instance variable which points to the Twirp generated
        # client object
        return twirp_func(self.rpc_client, serialized_input_obj)

    return wrapper


def deserialize_output(twirp_func:TWIRP_FUNCTION) -> TWIRP_FUNCTION:
    def wrapper(self, *args, **kwargs):
        resp = twirp_func(self, *args, **kwargs)

        return json.loads(MessageToJson(resp))

    return wrapper

def _get_input_object_name(twirp_func, namara_lib_instance):
    ''' This funtion will return a string of the form `<service_name>.<function_input_object>`
        ie. `organizations.GetOrganizationReqeust`

        It  relies on _name_ of the second parameter of `twirp_func`.
        This depends on parameter names being the same as the actual protofbuf objects
        i.e get_organization_request, which camelized will give
        GetOrganizationRequest, which is the correct input proto object for
        `get_organization` method.

        This will work as long the python twirp client generates the function
        signatures in this manner (which is stable and shouldn't be a problem)
    '''

    input_obj_name = inspect.getargspec(twirp_func).args[1]
    proper_input_name = to_camel_case(input_obj_name)

    cls_name = type(namara_lib_instance.rpc_client).__name__
    internal_var_name = '_' + cls_name + '__service_name'
    service_name = getattr(namara_lib_instance.rpc_client, internal_var_name).split('.')[0]

    return service_name + '.' + proper_input_name

# METHOD GENERATOR
for rpc_client, lib_cls in RPC_CLIENT_TO_LIB_CLS_MAPPING.items():
    for func_name, twirp_func in rpc_client.__dict__.items():
        if func_name.startswith('_'):
            continue

        # create decorators around the existing twirp functions to handle input
        # and output in a pythonic way (ie. using python objects rather than Twirp objects)
        new_func = deserialize_output(serialize_input(twirp_func))
        setattr(lib_cls, func_name, new_func)