Search code examples
pythonpython-3.xdecoratorpython-decoratorshug

Making Python decorators work with the Hug API framework


I am fairly new to Python. I am building a simple API using Hug. I am trying to use a decorator to handle all not handled exceptions as in the code below. But it appears I am not passing inputs required by Hug in the decorator properly.

auth.py

from functools import wraps

import hug
from falcon import HTTP_400, HTTP_500

import store
import validator
from user_entity import UserEntity


def _error(dict, response, status=HTTP_400):
    response.status = status
    return {'errors': dict}


def handle_exceptions(f):
    """Handle all non-handled exceptions."""
    @wraps(f)
    def decorated(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except Exception as e:
            return _error({'message': str(e)}, HTTP_500)
    return decorated


@hug.post('/')
@handle_exceptions
def create_user(username, password, response):
    """Validate and create a user in the database."""
    is_valid, vres = validator.validate_user(username, password)
    if not is_valid:
        return _error(
            {k: v for k, v in vres.items() if v is not None}, response)

    user = UserEntity(username=username, password=password)
    urn, usr = user.db_view()
    store.create_user(urn, usr)

    return user.public_view()

Here is the error I get:

Traceback (most recent call last):  
  File "auth.py", line 23, in decorated  
    return f(*args, **kwargs)  
TypeError: create_user() missing 1 required positional argument: 'response'  

During handling of the above exception, another exception occurred: 

Traceback (most recent call last):  
  File   "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/wsgiref/handlers.py", line 137, in run  
    self.result = application(self.environ, self.start_response)  
  File "/Users/munjal/.virtualenvs/utl-identity-auth-server/lib/python3.6/site-packages/falcon/api.py", line 189, in __call__  
    responder(req, resp, **params)  
  File "/Users/munjal/.virtualenvs/utl-identity-auth-server/lib/python3.6/site-packages/hug/interface.py", line 651, in __call__  
    self.render_content(self.call_function(**input_parameters), request, response, **kwargs)  
  File "/Users/munjal/.virtualenvs/utl-identity-auth-server/lib/python3.6/site-packages/hug/interface.py", line 595, in call_function  
    return self.interface(**parameters)  
  File "/Users/munjal/.virtualenvs/utl-identity-auth-server/lib/python3.6/site-packages/hug/interface.py", line 117, in __call__  
    return __hug_internal_self._function(*args, **kwargs)  
  File "auth.py", line 25, in decorated  
    return _error({'message': str(e)}, HTTP_500)  
  File "auth.py", line 14, in _error  
    response.status = status  
AttributeError: 'str' object has no attribute 'status'  

Solution

  • You forgot the response parameter in

    return _error({'message': str(e)}, HTTP_500)
    

    And I don't think the decorator works in general. Hug identifies the necessary parameters by function.__code__.co_varnames. This is not modified by functools.wraps. After you use your decorator, what hug.post can see is a function with arguments *args, *kwargs, which is not helpful.

    You may chain your router with ExceptionRouter