2

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'  
halfer
  • 19,824
  • 17
  • 99
  • 186
Moon
  • 33,439
  • 20
  • 81
  • 132

2 Answers2

3

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

Liteye
  • 2,761
  • 1
  • 16
  • 16
1

I have exactly the same need. I've found a way to do it although I'm not sure it's ideal.

  def handle_exceptions(f):
     @wraps(f)
     def decorated(*args, response, **kwargs):
        try:
            return f(*args, response=response, **kwargs)
        except MyError as e:
            response.status = HTTP_400
            return {'errors': str(e)}
        except Exception as e:
            response.status = HTTP_500
            return {'errors': 'internal server error'}
     return decorated


  @hug.post('/login')
  def login(user_id: btext, user_email: btext, request, response):
      load_session_data(request, response)
      validate_user(user_id, user_email)
      assert 3 < 2
      update_session_data(request, response, user_id=user_id, user_email=user_email)

The test 'assert' shows the returned content is 'internal server error' instead of the default 'A server error occurred. Please contact the administrator.'

The wraps is hug.decorators.wraps, not functools.wraps. The latter would return the default 'A server error occurred. Please contact the administrator.'

zpz
  • 354
  • 1
  • 3
  • 16