我想要有条件地应用装饰器(Flask-HTTPAuth的login_required)。如果sky_is_blue == True,我想应用装饰器,如果为False,则不会。因为它可能会在应用程序的生命周期中发生变化(实际上在实践中并不那么多,但绝对是为了单元测试的目的,而且我对任何情况下的原因都很好奇),所以这需要在调用时发生。有条件地应用Flask-HTTPAuth的login_required修饰器
所以我在装饰器中包装了装饰器。
行为与False情况(不应用装饰器)中的预期行为一样,但在True情况下应用装饰器时遇到问题。我不确定这是我做错了什么,或者与Flask-HTTPAuth发生了奇怪的交互。
以下脚本演示了两个单元测试的问题。 test_sky_not_blue通过,但test_sky_blue失败并显示堆栈跟踪。
from flask import Flask
from flask.ext.httpauth import HTTPBasicAuth
from functools import update_wrapper, wraps
from flask.ext.testing import TestCase
import unittest
app = Flask(__name__)
app.config['TESTING'] = True
sky_is_blue = True
auth = HTTPBasicAuth()
class ConditionalAuth(object):
def __init__(self, decorator):
print("ini with {}".format(decorator.__name__))
self.decorator = decorator
update_wrapper(self, decorator)
def __call__(self, func):
print("__call__: ".format(func.__name__))
@wraps(func)
def wrapped(*args, **kwargs):
print("Wrapped call, function {}".format(func.__name__))
if sky_is_blue:
rv = self.decorator(func(*args, **kwargs))
return rv
else:
rv = func(*args, **kwargs)
return rv
return wrapped
@app.route('/')
@ConditionalAuth(auth.login_required)
def index():
"""
Get a token
"""
return "OK"
class TestSky(TestCase):
def create_app(self):
return app
def test_sky_blue(self):
global sky_is_blue
sky_is_blue = True
response = self.client.get('/')
self.assert200(response)
def test_sky_not_blue(self):
global sky_is_blue
sky_is_blue = False
response = self.client.get('/')
self.assert200(response)
def suite():
return unittest.makeSuite(TestSky)
if __name__ == '__main__':
unittest.main(defaultTest='suite')
完整的堆栈跟踪我得到的是:
Traceback (most recent call last):
File "test.py", line 64, in test_sky_blue
response = self.client.get('/')
File "/usr/local/lib/python2.7/site-packages/werkzeug/test.py", line 778, in get
return self.open(*args, **kw)
File "/usr/local/lib/python2.7/site-packages/flask/testing.py", line 108, in open
follow_redirects=follow_redirects)
File "/usr/local/lib/python2.7/site-packages/werkzeug/test.py", line 751, in open
response = self.run_wsgi_app(environ, buffered=buffered)
File "/usr/local/lib/python2.7/site-packages/werkzeug/test.py", line 668, in run_wsgi_app
rv = run_wsgi_app(self.application, environ, buffered=buffered)
File "/usr/local/lib/python2.7/site-packages/werkzeug/test.py", line 871, in run_wsgi_app
app_rv = app(environ, start_response)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1836, in __call__
return self.wsgi_app(environ, start_response)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1820, in wsgi_app
response = self.make_response(self.handle_exception(e))
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1403, in handle_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1817, in wsgi_app
response = self.full_dispatch_request()
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1477, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1381, in handle_user_exception
reraise(exc_type, exc_value, tb)
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1475, in full_dispatch_request
rv = self.dispatch_request()
File "/usr/local/lib/python2.7/site-packages/flask/app.py", line 1461, in dispatch_request
return self.view_functions[rule.endpoint](**req.view_args)
File "test.py", line 40, in wrapped
rv = self.decorator(func(*args, **kwargs))
File "/usr/local/lib/python2.7/site-packages/flask_httpauth.py", line 48, in login_required
@wraps(f)
File "/usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versions/2.7/lib/python2.7/functools.py", line 33, in update_wrapper
setattr(wrapper, attr, getattr(wrapped, attr))
AttributeError: 'str' object has no attribute '__module__'
测试与Python 2.7.11,烧瓶HTTPAuth == 2.7.1,瓶== 0.10.1,任何见解将不胜赞赏。
谢谢Miguel,特别是你在Flask-HTTPAuth上的工作!这是一个很好的建议,但是我想在这种情况下使用一个通用的解决方案,因为我还想有条件地应用[Flask-TokenAuth](https://github.com/al4/flask-tokenauth),我编写的模块是基于Flask-HTTPAuth和您的博客文章。最初我是通过对它进行分类来开始的,但是决定它已经变得足够不同以保证一个新的模块。 –