2017-08-03 65 views
0

我一直試圖從語言的角度來理解Flask裝飾器。基於我對裝飾器的理解,方法1,方法2和方法3應該具有相同的性質。擴展裝飾器後來手動崩潰程序

# 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方法。

一些基本的研究表明,其他人都遇到過這種「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」?

這種行爲看起來完全不合邏輯。

(或者我誤解一個裝飾做什麼,它實際上是把某種隱藏的元數據到結果和瓶正被邪惡和檢查無證方式的元數據。)

回答

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功能。

+0

不,方法1不調用回調。添加一個斷言回調,你會發現它不是從method1調用。 Flask在所有3種方法中調用回調函數。 –

+0

總而言之,裝飾者正在做着讓Flask高興的東西,但它並沒有調用回調。該回調稍後將通過werkzeug/test模塊進行調用。 –

+0

當然方法1正在調用回調。這就是它工作的原因,以及爲什麼當你訪問'/ 1'時你會看到'RESPONSE'。我建議刪除這些評論並將它們移到你的問題中,在那裏你可以格式化它們,使它們可讀。請注意,我並不是聲稱* decorator *正在調用'callback';請再看看這個答案(並注意這裏提供的代碼有效)。 – larsks