Google App Engine中的Python函数装饰器
我在使用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 个回答
装饰器是在 Python 2.4 版本中加入的(我记得是这个时候),也许 googleapp 用的是一个旧版本?
你也可以这样做:
def content():
print "Release sensitive data!"
content = require_auth(content)
这样做的效果和装饰器是一样的,只是稍微麻烦一些。
@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
也没有做什么特别的事情。所以我想象你可能尝试运行的代码和你展示给我们的不一样...!-)
你在调用 content()
时没有传入任何参数,但被装饰过的版本 protected_view
需要一个 request
参数。要么给 content
加上这个参数,要么把它从 protected_view
中去掉。
如果你在简单版本中遇到这个错误,我会怀疑 content
是一个类方法,就像 Alex 提到的那样。否则,看起来你是告诉它至少需要一个参数,但实际上没有给它任何参数。