Search code examples
python-3.xmethodsgrpcpython-decoratorsgrpc-python

python3 - decorator function: Variable referenced before assignment


Im working on a gRPC microservice.

Because every method has to load a JSON string from the request argument first – and then in the end dump it again, I want to use a decorator on the methods of the class, so that the method itself besides the return only contains the ... more stuff to do ... below:

class VPPSetupService(SetupService_pb2_grpc.VPPSetupServiceServicer):
...
   def method(self, request, context):
        request = json.loads(request.context)
        request = request["request"]
        header = request["headers"]
        ... more stuff to do ...
        response = json.dumps(response)
        return SetupService_pb2.JsonContextResponse(context=response)

So I wrote a decorator function. However, I could not find a solution for this error:

request = json.loads(request.context)

UnboundLocalError: local variable 'request' referenced before assignment

The error is produced by this: (Just as an example, the real thing is a bit more complex)

from functools import wraps

def json_and_error(func):
        @wraps(func)
        def args_wrapper(*args, **kwargs):           # Problem: This
            request = json.loads(request.context)  <<# variable is referenced
            request = request["request"]             # before assignment
            header = request["headers"]
            func(*args, **kwargs)
            return func(*args, **kwargs)
        return args_wrapper

class VPPSetupService(SetupService_pb2_grpc.VPPSetupServiceServicer):
    ...
       @json_and_error
       def method(self, request, context):
            ... more stuff to do ...
            return SetupService_pb2.JsonContextResponse(context=response)

I tried using request = json.loads(args[1].context) instead. But then I get this error:

if request["source_machine"] and request["dest_machine"]: TypeError:

'JsonContextRequest' object is not subscriptable

The input given as request argument is an object of type <class 'SetupService_pb2.JsonContextRequest'> The JSON String in the request would be accessible through the request.context attribute.

I'm thinking the problem is related to how the decorator function is being called. I suppose if it was called at runtime of the class method the variable request should already have been assigned the request object.

But maybe I'm completely wrong here. So how would you solve this?


Solution

  • There are few errors in your code:

    def args_wrapper(*args, **kwargs):
        request = json.loads(request.context)
    

    You're using request.context while request variable is undefined (it will be defined only after json.loads(request.context) will be executed). You tried to fix it using request = json.loads(args[1].context), but got JsonContextRequest object is not subscriptable in wrapped function. Look at this line closer:

    return func(*args, **kwargs)
    

    You decorator returns wrapped function called with the same params, so this code result isn't used, you don't pass request and header to wrapped function:

    request = json.loads(request.context)
    request = request["request"]
    header = request["headers"]
    

    Also you shouldn't call wrapped function:

    func(*args, **kwargs)
    

    I guess you want to do something like this:

    def json_and_error(func):
        def args_wrapper(this, request):
            context = request.context
            request = json.loads(context)
            request = request["request"]
    
            return func(this, request, context)
    
        return args_wrapper