手动扩展装饰器会导致程序崩溃

时间:2017-08-03 22:32:15

标签: python flask gunicorn

我一直试图从语言的角度理解Flask装饰器。基于我对装饰器的理解,method1,method2和method3应该具有相同的功能。

# server.py
#
# callback-based server response, based on official Flask docs:
# http://flask.pocoo.org/snippets/8/
#
# to run server:
#   gunicorn server:app --bind localhost:5000
#
# to test:
#   curl http://localhost:5000/1 -X POST -d POST_DATA -H "Content-Type: application/json"
#   curl http://localhost:5000/2 -X POST -d POST_DATA -H "Content-Type: application/json"
#   curl http://localhost:5000/3 -X POST -d POST_DATA -H "Content-Type: application/json"




from flask import Flask, request
app = Flask(__name__)


def callback(*args, **kwargs):
    print "post_data=", request.data
    return "RESPONSE\n", 200

def wrapper(f):
    return callback




# method 1
@app.route('/1', methods=['POST'])
@wrapper
def method1():
    pass


# method 2
@app.route("/2", methods=['POST'])
def method2():
    return wrapper(method2)


# method 3
@app.route("/3", methods=['POST'])
def method3():
    return callback

但是,当我进行3次卷曲测试时,结果会有所不同。在/ 1和/ 2的情况下,程序在打印“post_data = POST_DATA”后不久崩溃:

post_data= POST_DATA
[2017-08-03 15:20:26,625] ERROR in app: Exception on /3 [POST]
Traceback (most recent call last):
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1982, in wsgi_app
    response = self.full_dispatch_request()
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1615, in full_dispatch_request
    return self.finalize_request(rv)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1630, in finalize_request
    response = self.make_response(rv)
  File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 1740, in make_response
    rv = self.response_class.force_type(rv, request.environ)
  File "/usr/local/lib/python2.7/dist-packages/werkzeug/wrappers.py", line 885, in force_type
    response = BaseResponse(*_run_wsgi_app(response, environ))
  File "/usr/local/lib/python2.7/dist-packages/werkzeug/test.py", line 903, in run_wsgi_app
    buffer.append(next(app_iter))
StopIteration

请注意,在完全退出app.route方法后,它在Flask中死了。

一些基础研究表明其他人遇到过这种“StopIteration”问题: https://github.com/getsentry/raven-python/issues/514 但不是在这样一个简单的例子中,也不是在使用官方烧瓶文档(http://flask.pocoo.org/snippets/8/)上的响应回调模式的任何示例中。因此我得出结论,问题不在于Flask本身,而在于我对装饰器的理解。

我不明白这3种方法可能会做出不同的事情。所有这些都引用了完全相同的“回调”功能。要么他们成功地将这个功能提供给Flask,要么他们没有。

如果他们确实将功能提供给Flask ...则测试成功。 如果他们没有将功能提供给Flask ......那么Flask如何调用函数来打印“post_data = POST_DATA”?

这种行为似乎完全不合逻辑。

(或者我误解了装饰器的作用,实际上它将某种隐藏的元数据放入结果中,Flask是邪恶的,并以未记录的方式检查元数据。)

1 个答案:

答案 0 :(得分:1)

您的代码存在基本问题。首先,回想一下装饰者......

@decorator
def myfunction():
    pass

......完全等同于:

def _myfunction():
    pass
myfunction = decorator(_myfunction)

考虑到这一点,您将注意到在方法2中,您将返回一个函数,而不是迭代器:

@app.route("/2", methods=['POST'])
def method2():
    return wrapper(method2)

请记住,wrapper的返回值是函数

def wrapper(f):
    return callback

所以在这里你有效地做了:

@app.route("/2", methods=['POST'])
def method2():
    return callback

您将返回单个值(函数)而不是元组。你真的想要:

@app.route("/2", methods=['POST'])
def method2():
    return wrapper(method2)()

类似于方法3:

@app.route("/3", methods=['POST'])
def method3():
    return callback()

在这两种情况下,您都需要实际调用 callback函数。