Search code examples
pythoncherrypy

passing function to a decorator in cherrypy


How do I pass the index() to the decorator function called uppercase(). I want the value passed from index() to be capitalized.

I am getting this error:

500 Internal Server Error
The server encountered an unexpected condition which prevented it from fulfilling the request.

Traceback (most recent call last):
  File "/home/user/.local/lib/python3.6/site-packages/cherrypy/_cprequest.py", line 638, in respond
    self._do_respond(path_info)
  File "/home/user/.local/lib/python3.6/site-packages/cherrypy/_cprequest.py", line 701, in _do_respond
    self.hooks.run('before_finalize')
  File "/home/user/.local/lib/python3.6/site-packages/cherrypy/_cprequest.py", line 95, in run
    self.run_hooks(iter(sorted(self[point])))
  File "/home/user/.local/lib/python3.6/site-packages/cherrypy/_cprequest.py", line 117, in run_hooks
    hook()
  File "/home/user/.local/lib/python3.6/site-packages/cherrypy/_cprequest.py", line 65, in __call__
    return self.callback(**self.kwargs)
TypeError: uppercase() missing 1 required positional argument: 'func'

import cherrypy
from cherrypy import tools

@cherrypy.tools.register('before_finalize')
def uppercase(func):
    def wrapper():
        original_result = func()
        modified_result = original_result.upper()
        return modified_result
    return wrapper

class HelloWorld(object):
    @cherrypy.expose
    @cherrypy.tools.uppercase()
    def index(self):
        return 'Hello!'


if __name__ == '__main__':
    cherrypy.tools.uppercase = cherrypy.Tool('before_finalize', uppercase)
    cherrypy.quickstart(HelloWorld())

Solution

  • You should use the decorator like this:

    @cherrypy.tools.uppercase
    def index(self):
        return 'Hello!'
    

    Note the lack of () after uppercase - a decorator takes the first argument (the decorated function) implicitly and the parentheses are not needed.

    Edit:

    According to the documentation, a tool can be defined using the cherrypy.tools.register decorator, or the cherrypy.Tool constructor. In your code, you define the uppercase tool twice.

    But, in your case the uppercase decorator does not need to be defined as a Tool, because it's not something that needs to be ran every time as a hook (before_finalize).

    Therefore, you will be better off with just using it as a plain Python decorator, like so:

    from functools import wraps
    
    # just a plain Python decorator
    def uppercase(func):
        @wraps(func) #preserve function attributes, such as its name
        def wrapper(*args):
            original_result = func(*args)
            modified_result = original_result.upper()
            return modified_result
        return wrapper
    
    class HelloWorld(object):
        @cherrypy.expose
        @uppercase # decorated once, the exposed function is now uppercase(index) 
        def index(self):
            return 'Hello!'