Search code examples
pythonclasscherrypypython-decorators

passing function into a class and using it as a decorator of class methods


first I created some user management functions I want to use everywhere, and bound them to cherrypy, thinking I could import cherrypy elsewhere and they would be there. Other functions seem to import fine this way, when not used as decorators.

from user import validuser
cherrypy.validuser = validuser
del validuser

that didn't work, so next I tried passing the function into the class that is a section of my cherrypy site (/analyze) from the top level class of pages:

class Root:
    analyze = Analyze(cherrypy.validuser) #maps to /analyze

And in the Analyze class, I referred to them. This works for normal functions but not for decorators. why not?

class Analyze:

    def __init__(self, validuser):
        self.validuser = validuser

    @cherrypy.expose
    @self.validuser(['uid'])
    def index(self, **kw):        
        return analysis_panel.pick_data_sets(user_id=kw['uid'])

I'm stuck. How can I pass functions in and use them as decorators. I'd rather not wrap my functions like this:

    return self.validuser(analysis_panel.pick_data_sets(user_id=kw['uid']),['uid'])

thanks.

ADDED/EDITED: here's what the decorator is doing, because as a separate issue, I don't think it is properly adding user_id into the kwargs

def validuser(old_function, fetch=['uid']):
    def new_function(*args, **kw):
        "... do stuff. decide is USER is logged in. return USER id or -1 ..."
        if USER != -1 and 'uid' in fetch:
            kw['uid'] = user_data['fc_uid']
        return old_function(*args, **kw)
    return new_function

only the kwargs that were passed in appear in the kwargs for the new_function. Anything I try to add isn't there. (what I'm doing appears to work here How can I pass a variable in a decorator to function's argument in a decorated function?)


Solution

  • The proper way in CherryPy to handle a situation like this is to have a tool and to enable that tool on the parts of your site that require authentication. Consider first creating this user-auth tool:

    @cherrypy.tools.register('before_handler')
    def validate_user():
        if USER == -1:
            return
        cherrypy.request.uid = user_data['fc_uid']
    

    Note that the 'register' decorator was added in CherryPy 5.5.0.

    Then, wherever you wish to validate the user, either decorate the handler with the tool:

    class Analyze:
    
        @cherrypy.expose
        @cherrypy.tools.validate_user()
        def index(self):
            return analysis_panel.pick_data_sets(user_id=cherrypy.request.uid)
    

    Or in your cherrypy config, enable that tool:

    config = {
        '/analyze': {
            'tools.validate_user.on': True,
        },
    }