python - 有条件地应用 Flask-HTTPAuth 的 login_required 装饰器

标签 python python-2.7 flask flask-httpauth

我正在尝试有条件地应用装饰器(Flask-HTTPAuth 的 login_required )。如果 sky_is_blue == True,我想应用装饰器,如果为 False,则不应用。

这需要随叫随到,因为它可能会在应用程序的生命周期内发生变化(实际上在实践中并没有那么多,但绝对是为了单元测试目的,我很好奇任何情况下的原因)。

所以我把装饰器包裹在装饰器中。

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、Flask-HTTPAuth==2.7.1、Flask==0.10.1 进行测试,如有任何见解,我们将不胜感激。

最佳答案

有趣的是,提出问题对于帮助人们解决问题是多么有效。

问题是装饰器调用中的括号:

rv = self.decorator(func(*args, **kwargs))

将其更改为以下修复它:

rv = self.decorator(func)(*args, **kwargs)

装饰器需要返回一个函数,但是通过直接将参数传递给 func() 我没有给它机会这样做。

我认为,将其分成一个单独的调用会使这一点更清楚:

decorated_function = self.decorator(func)
return decorated_function(*args, **kwargs))

关于python - 有条件地应用 Flask-HTTPAuth 的 login_required 装饰器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35379516/

相关文章:

python - 将 CNN 模型转化为类

python - 如何使用 nan 和 twinx/secondary_y 绘制多列线图

python - 无法在 __init__ 处向 ModelForm 添加字段

python - 如何从python中的递归函数返回值?

python-3.x - 发送带有内嵌图像的电子邮件 Flask-Mail?

amazon-web-services - 从Docker容器内部连接到RDS Postgresql DB时出错

python - 如何在 Python 3.6 上安装 PIP?

python - 使用 BeautifulSoup CSS 选择器获取文本

python - Python 2.7 和 3 之间的多重继承差异

python - 提交时更新按钮文本以延长加载时间