一個困擾我兩年的 Flask bug

作者:李輝,Web 開發(fā)者?
出處:greyli.com
嚴格來說算不上 bug,而是一個很容易導致出錯的設計。
具體行為是,如果你安裝了 python-dotenv,同時在 Flask 程序的上層目錄創(chuàng)建了 .env 或 .flaskenv 文件,那么你將沒法成功執(zhí)行 flask run 等命令,因為這會導致 Flask 沒法正確找到對應的 Flask 程序?qū)嵗?/span>
這個問題從 Flask 開始引入 CLI 機制開始就存在了,困擾了我兩年。18 年偶然在用戶根目錄創(chuàng)建了一個 .env 文件,發(fā)現(xiàn) Flask 程序沒法運行了,當時遇到的各種 bug 太多,沒仔細考慮這兩者之間的關(guān)聯(lián)。后來經(jīng)過幾次測試,才確定下來是上層目錄的 .env 和 .flaskenv 文件導致,但是一時找不到原因,就暫時放下了。直到 19 年 11 月,花了幾個小時排查,還是沒找到原因。
中間花了很長時間來追蹤 Windows 特定的 Flask 程序無法啟動的 bug(TypeError: environment can only contain strings),實在是怕了。因為 Flask 的 CLI 涉及太多東西,有時你要鉆進 python-dotenv(#101) 和 Werkzeug(#1320) 才能找到問題的原因。
但是問題不解決的話,你永遠睡不好覺。《Flask Web 開發(fā)實戰(zhàn)》第一部分的示例程序都放在了一個程序倉庫,而且都放在了子目錄,這意味著如果讀者錯誤的在倉庫根目錄創(chuàng)建 .env 和 .flaskenv 文件的話,就會導致子目錄下的六個示例程序沒法運行。前后大概收到 6 個相關(guān)的讀者反饋,雖然后續(xù)在網(wǎng)站上添加了提醒,在重印的書里介紹創(chuàng)建 .env/.flaskenv 文件的地方追加提醒,但這終究沒有真正解決問題,而且總會有人可以完美的錯過所有提示。
如果每個人都可以在書上標記出錯位置并共享,那么這一頁應該會有很多紅色小叉號(參考《超級馬里奧制造》,也許未來某個電子書平臺會做出來這個功能 :P)。
前幾天在這個 Issue 的提醒下,又花了兩個小時排查,這次終于找到原因。加上寫 PR(#3560)和 Issue(#3561),前后兩年一共花了 8 個小時,這個問題終于有了著落。沒意外的話,預計會在下一個版本的 Flask 中更新。下面是具體原因。
本來以為這個問題和 python-dotenv 或 Werkzeug 相關(guān),沒想到只是 Flask 本身代碼的問題,我太笨了,這個問題本可以早一點解決。
按照預定的行為,當安裝了 python-dotenv,F(xiàn)lask 會自動加載 .env 和 .flaskenv 里的環(huán)境變量。python-dotenv 在搜索存儲環(huán)境變量的文件時,會從當前目錄開始向上搜索,如果找到就返回對應的文件路徑。但是這時 Flask 如果發(fā)現(xiàn) .env 或 .flaskenv 的所在目錄不是當前目錄,就會把當前工作目錄切換到 .env 和 .flaskenv 所在的目錄(相關(guān)源碼)。而如果你的程序模塊或程序包不是和 .env/.flaskenv 同級目錄的話,就會導致找不到程序?qū)嵗?/span>
使用下面的步驟可以重現(xiàn):
$?git?clone?https://github.com/greyli/flask-env-test
$?cd?flask-env-test
$?pip?install?-r?requirements.txt??#?or?just?pip?install?flask[dotenv]
$?cd?hello
$?flask?run
示例項目的文件結(jié)構(gòu)如下:
-?flask-env-test
????-?.env
????-?hello
????????-?app.py
像示例程序這樣把程序存儲在 app.py 文件中時,運行 flask run 你會看到下面的報錯:
$?flask?run
?*?Environment:?production
???WARNING:?This?is?a?development?server.?Do?not?use?it?in?a?production?deployment.
???Use?a?production?WSGI?server?instead.
?*?Debug?mode:?off
Usage:?flask?run?[OPTIONS]
?
Error:?Could?not?locate?a?Flask?application.?You?did?not?provide?the?"FLASK_APP"?environment?variable,?and?a?"wsgi.py"?or?"app.py"?module?was?not?found?in?the?current?directory.
如果你使用 FLASK_APP 指定了程序的導入路徑,那么錯誤大概會是這樣:
$?flask?run
?*?Serving?Flask?app?"myapp"
?*?Environment:?production
???WARNING:?This?is?a?development?server.?Do?not?use?it?in?a?production?deployment.
???Use?a?production?WSGI?server?instead.
?*?Debug?mode:?off
Usage:?flask?run?[OPTIONS]
?
Error:?Could?not?import?"myapp".
相關(guān)鏈接
- https://github.com/greyli/helloflask/issues/200
- https://github.com/pallets/flask/issues/3561https://github.com/pallets/flask/pull/3560
好文章,我在看??
