Can't call a decorator within the imported sub

2019-06-11 08:56发布

问题:

I am using cherrypy as a web server, and I want to check a user's logged-in status before returning the page. This works on methods in the main Application class (in site.py) but gives an error when I call the same decorated function on method in a class that is one layer deeper in the webpage tree (in a separate file).

validate_user() is the function used as a decorator. It either passes a user to the page or sends them to a 401 restricted page, as a cherrypy.Tool, like this:

from user import validate_user
cherrypy.tools.validate_user = cherrypy.Tool('before_handler', validate_user)

I attach different sections of the site to the main site.py file's Application class by assigning instances of the sub-classes as variables accordingly:

from user import UserAuthentication

class Root:
    user = UserAuthentication() # maps user/login, user/register, user/logout, etc
    admin = Admin()
    api = Api()

    @cherrypy.expose
    @cherrypy.tools.validate_user()
    def how_to(self, **kw):
        from other_stuff import how_to_page
        return how_to_page(kw) 

This, however, does not work when I try to use the validate_user() inside the Admin or Api or Analysis sections. These are in separate files.

import cherrypy

class Analyze:
    @cherrypy.expose
    @cherrypy.tools.validate_user() #### THIS LINE GIVES ERROR ####
    def explore(self, *args, **kw): # @addkw(fetch=['uid'])
        import explore
        kw['uid'] = cherrypy.session.get('uid',-1)
        return explore.explorer(args, kw)

The error is that cherrypy.tools doesn't have a validate_user function or method. But other things I assign in site.py do appear in cherrypy here. What's the reason why I can't use this tool in a separate file that is part of my overall site map?

If this is relevant, the validate_user() function simply looks at the cherrypy.request.cookie, finds the 'session_token' value, and compares it to our database and passes it along if the ID matches.

Sorry I don't know if the Analyze() and Api() and User() pages are subclasses, or nested classes, or extended methods, or what. So I can't give this a precise title. Do I need to pass in the parent class to them somehow?

回答1:

The issue here is that Python processes everything except the function/method bodies during import. So in site.py, when you import user (or from user import <anything>), that causes all of the user module to be processed before the Python interpreter has gotten to the definition of the validate_user tool, including the decorator, which is attempting to access that tool by value (rather than by a reference).

CherryPy has another mechanism for decorating functions with config that will enable tools on those handlers. Instead of @cherrypy.tools.validate_user, use:

@cherrypy.config(**{"tools.validate_user.on": True})

This decorator works because instead of needing to access validate_user from cherrypy.tools to install itself on the handler, it instead configures CherryPy to install that tool on the handler later, when the handler is invoked.

If that tool is needed for all methods on that class, you can use that config decorator on the class itself.

You could alternatively, enable that tool for given endpoints in the server config, as mentioned in the other question.