Google App Engine中的Python函数装饰器

2 投票
4 回答
2187 浏览
提问于 2025-04-15 12:53

我在使用Python的函数装饰器时遇到了一些麻烦,特别是在谷歌的AppEngine上。我对装饰器不太熟悉,但我觉得它们在网页编程中很有用,比如在执行某些功能之前强制用户登录。

总之,我在这里跟着一个flickr登录的例子学习,这个例子使用了django,并且用装饰器来要求用户登录flickr。但是我在AppEngine中似乎无法让这种装饰器正常工作。

我把问题简化成这样:

def require_auth(func):
    def check_auth(*args, **kwargs):
        print "Authenticated."
        return func(*args, **kwargs)
    return check_auth

@require_auth
def content():
    print "Release sensitive data!"

content()

这段代码在命令行中可以正常运行,但当我在GoogleAppEngineLauncher(OS X)中运行时,却出现了以下错误:

check_auth() takes at least 1 argument (0 given) 

我也不太明白为什么会这样……

我更新了一下,加入了实际的代码: @asperous.us 我把content()的定义改成了可以接收可变参数,这样做是你想要的意思吗? @Alex Martelli,'print'在AppEngine中确实可以用,但这也是一个完全合理的批评。 就像我说的,我想使用上面链接中的flickr登录。我试着把它放到我的应用里,像这样:

def require_flickr_auth(view):
    def protected_view(request,*args, **kwargs):
        if 'token' in request.session:
            token = request.session['token']
            log.info('Getting token from session: %s' % token)
        else:
            token = None
            log.info('No token in session')

        f = flickrapi.FlickrAPI(api_key, api_secret,
                                token=token, store_token=False)

        if token:
            # We have a token, but it might not be valid
            log.info('Verifying token')
            try:
                f.auth_checkToken()
            except flickrapi.FlickrError:
                token = None
                del request.session['token']

        if not token:
            # No valid token, so redirect to Flickr
            log.info('Redirecting user to Flickr to get frob')
            url = f.web_login_url(perms='read')
            print "Redirect to %s" % url

        # If the token is valid, we can call the decorated view.
        log.info('Token is valid')
        return view(request,*args, **kwargs)

    return protected_view

@require_flickr_auth
def content(*args, **kwargs):
    print 'Welcome, oh authenticated user!'

def main():
    print 'Content-Type: text/plain'
    content()

if __name__ == "__main__":
    main()

当我去掉@require_flickr_auth这个装饰器时,字符串'Welcome ...'就能正常打印出来。否则,我就会看到一个很难看的AppEngine异常页面,底部有

type 'exceptions.TypeError': protected_view() takes at least 1 argument (0 given) 

的内容。

4 个回答

0

装饰器是在 Python 2.4 版本中加入的(我记得是这个时候),也许 googleapp 用的是一个旧版本?

你也可以这样做:

def content():
    print "Release sensitive data!"
    content = require_auth(content)

这样做的效果和装饰器是一样的,只是稍微麻烦一些。

1

@Owen,"至少需要一个参数"的错误提示说明你是在一个类里面定义内容(也就是作为一个方法),而不是像你展示的那样作为一个普通的函数。你到底是怎么在GAE上执行这段代码的呢?也就是说,你的app.yaml是什么样的?如果你把你的代码完全按照你给的格式放在silly.py里,而在你的app.yaml中有:

handlers:
- url: /silly
  script: silly.py

那么当你访问yourapp.appspot.com/silly时,你在浏览器和日志中都不会看到任何东西(除了日志中的"GET /silly HTTP/1.1" 200 -,当然;-):没有错误,但print也没有做什么特别的事情。所以我想象你可能尝试运行的代码和你展示给我们的不一样...!-)

3

你在调用 content() 时没有传入任何参数,但被装饰过的版本 protected_view 需要一个 request 参数。要么给 content 加上这个参数,要么把它从 protected_view 中去掉。

如果你在简单版本中遇到这个错误,我会怀疑 content 是一个类方法,就像 Alex 提到的那样。否则,看起来你是告诉它至少需要一个参数,但实际上没有给它任何参数。

撰写回答