เหตุใด flask CLI จึงแนะนำให้ใช้กับ Flask.run


13

ใน Flask 0.11 มีการflaskแนะนำ CLI ขอแนะนำให้ใช้ทั้งเอกสารและสถานะการเปลี่ยนแปลง

เอกสารเซิร์ฟเวอร์การพัฒนา :

เริ่มต้นด้วย Flask 0.11 มีหลายวิธีในการรันเซิร์ฟเวอร์การพัฒนา ที่ดีที่สุดคือโปรแกรมอรรถประโยชน์บรรทัดคำสั่งของขวดแต่คุณยังสามารถใช้Flask.run()วิธีการต่อ

บรรทัดคำสั่ง

ขวดสคริปต์บรรทัดคำสั่ง (บรรทัดคำสั่ง Interface) ขอแนะนำอย่างยิ่งสำหรับการพัฒนาเพราะมีประสบการณ์ที่เหนือกว่าโหลดเนื่องจากวิธีการโหลดแอพลิเคชัน การใช้งานพื้นฐานมีดังนี้:

$ export FLASK_APP=my_application
$ export FLASK_DEBUG=1
$ flask run

รายการเปลี่ยนแปลง :

  • เพิ่มflaskและflask.cliโมดูลเพื่อเริ่มต้นเซิร์ฟเวอร์ดีบักโลคัลผ่านระบบคลิก CLI แนะนำให้ใช้กับflask.run()วิธีเก่าเนื่องจากทำงานได้เร็วขึ้นและเชื่อถือได้มากขึ้นเนื่องจากมีการออกแบบที่แตกต่างและแทนที่Flask-Scriptด้วย

จนถึงตอนนี้ฉันไม่ได้สังเกตุว่า "ประสบการณ์รีโหลดที่เหนือกว่า" ฉันไม่เห็นจุดใช้ CLI เหนือสคริปต์ที่กำหนดเอง

ถ้าใช้Flask.runฉันก็จะเขียนไฟล์หลาม:

#!/usr/bin/env python3
from my_app import app


if __name__ == '__main__':
    app.run(debug=True)

หากใช้ CLI จะต้องระบุตัวแปรสภาพแวดล้อม ในเอกสารของ CLI ระบุว่าสิ่งนี้สามารถรวมเข้ากับactivateสคริปต์ของ virtualenvwrapper ส่วนตัวฉันถือว่านี่เป็นส่วนหนึ่งของแอปพลิเคชันและคิดว่ามันควรจะอยู่ภายใต้การควบคุมเวอร์ชัน อนิจจาจำเป็นต้องใช้เชลล์สคริปต์:

#!/usr/bin/env bash
export FLASK_APP=my_app:app
export FLASK_DEBUG=1

flask run

แน่นอนว่าสิ่งนี้จะมาพร้อมกับสคริปต์ค้างคาวเพิ่มเติมทันทีที่ผู้ใช้ Windows คนใดคนหนึ่งเริ่มทำงานร่วมกัน

นอกจากนี้ตัวเลือกแรกยังอนุญาตให้ติดตั้งเขียนใน Python ก่อนเริ่มแอพจริง

สิ่งนี้ยอมให้ตัวอย่าง

  • เพื่อแยกอาร์กิวเมนต์บรรทัดคำสั่งใน Python
  • เพื่อตั้งค่าการบันทึกก่อนเรียกใช้แอพ

ดูเหมือนว่าพวกเขาส่งเสริมว่าเป็นไปได้ที่จะเพิ่มคำสั่งที่กำหนดเอง ฉันล้มเหลวที่จะดูว่าทำไมสิ่งนี้จึงดีกว่าการเขียนสคริปต์ Python แบบง่าย ๆ ซึ่งมีทางเลือกให้เปิดเผยผ่านจุดเข้าใช้งาน

ตัวอย่างเอาต์พุตการบันทึกเมื่อใช้ตัวบันทึกที่กำหนดค่าโดยใช้สคริปต์การเรียกใช้ Python:

$ ./run.py 
   DEBUG 21:51:22 main.py:95) Configured logging
    INFO 21:51:22 _internal.py:87)  * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
    INFO 21:51:22 _internal.py:87)  * Restarting with inotify reloader
   DEBUG 21:51:22 main.py:95) Configured logging
 WARNING 21:51:22 _internal.py:87)  * Debugger is active!
    INFO 21:51:22 _internal.py:87)  * Debugger pin code: 263-225-431
   DEBUG 21:51:25 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
   DEBUG 21:51:25 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
    INFO 21:51:25 _internal.py:87)  * Detected change in 'my_app/main.py', reloading
    INFO 21:51:26 _internal.py:87)  * Restarting with inotify reloader
   DEBUG 21:51:26 main.py:95) Configured logging
 WARNING 21:51:26 _internal.py:87)  * Debugger is active!
    INFO 21:51:26 _internal.py:87)  * Debugger pin code: 263-225-431

ตัวอย่างเอาต์พุตการบันทึกเมื่อใช้ตัวบันทึกที่กำหนดค่าโดยใช้ CLI: โปรดสังเกตว่าตัวบันทึกรากไม่สามารถตั้งค่าได้เร็วพอในกระบวนการ

$ ./run.sh 
 * Serving Flask app "appsemble.api.main:app"
 * Forcing debug mode on
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
 * Restarting with inotify reloader
   DEBUG 21:51:33 main.py:95) Configured logging
 * Debugger is active!
 * Debugger pin code: 187-758-498
   DEBUG 21:51:34 main.py:95) Configured logging
   DEBUG 21:51:37 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
   DEBUG 21:51:37 inotify_buffer.py:61) in-event <InotifyEvent: src_path=b'my_app/main.py', wd=272, mask=IN_MODIFY, cookie=0, name=b'main.py'>
 * Detected change in 'my_app/main.py', reloading
    INFO 21:51:37 _internal.py:87)  * Detected change in 'my_app/main.py', reloading
 * Restarting with inotify reloader
    INFO 21:51:38 _internal.py:87)  * Restarting with inotify reloader
 * Debugger is active!
 * Debugger pin code: 187-758-498
   DEBUG 21:51:38 main.py:95) Configured logging

คำถามจริงของฉันคือ:

ทำไมขวด CLI ถึงแนะนำมากกว่าFlask.run?

คำตอบ:


11

ในเอกสารเซิร์ฟเวอร์การพัฒนาพวกเขาระบุว่ามีปัญหากับการเรียกใช้ run () และโหลดรหัสใหม่โดยอัตโนมัติ:

วิธีนี้ใช้ได้ผลดีกับกรณีทั่วไป แต่ใช้งานไม่ได้กับการพัฒนาซึ่งเป็นสาเหตุที่ทำให้ Flask 0.11 เป็นต้นไปแนะนำวิธีการใช้ขวด สาเหตุของเรื่องนี้คือเนื่องจากกลไกการโหลดซ้ำมีผลข้างเคียงที่แปลกประหลาด (เช่นการรันโค้ดบางอย่างสองครั้งบางครั้งก็ล้มเหลวโดยไม่มีข้อความหรือกำลังจะตายเมื่อเกิดข้อผิดพลาดทางไวยากรณ์หรือการนำเข้า)

พวกเขาอ้างว่า CLI ไม่ประสบปัญหานี้

ความมุ่งมั่นแรกที่ดูเหมือนจะสัมผัสกับปัญหานี้คือ: https://github.com/pallets/flask/commit/3bdb90f06b9d3167320180d4a5055dcd949bf72f

และที่นั่น Armin Ronacher เขียนว่า:

ไม่แนะนำให้ใช้ฟังก์ชั่นนี้ในการพัฒนาด้วยการโหลดซ้ำอัตโนมัติเนื่องจากไม่รองรับฟังก์ชั่นนี้ แต่คุณควรใช้การสนับสนุนflaskของสคริปต์บรรทัดคำสั่งrunserver

ดังที่ Aaron Hall กล่าวไว้ดูเหมือนว่าการใช้ run () อาจมีปัญหาเนื่องจากความจริงที่ว่าวัตถุทั้งหมดซึ่งเป็นอินสแตนซ์ของคลาสที่กำหนดไว้ในโมดูลที่ถูกแทนที่จะไม่ถูกคืนสภาพใหม่และเมื่อใดก็ตามที่โหลดโมดูล โมดูลที่นำเข้าไม่ได้รับการโหลดซ้ำเช่นกัน

รายละเอียดเกี่ยวกับเรื่องนี้อาจพบได้ใน Python 3 ที่: https://docs.python.org/3/library/importlib.html?highlight=importlib#module-importlib

มันระบุว่า:

เช่นเดียวกับวัตถุอื่น ๆ ใน Python วัตถุเก่าจะถูกเรียกคืนเฉพาะหลังจากการนับการอ้างอิงของพวกเขาลดลงถึงศูนย์

การอ้างอิงอื่น ๆ ไปยังวัตถุเก่า (เช่นชื่อภายนอกโมดูล) จะไม่ตอบสนองเพื่ออ้างถึงวัตถุใหม่และต้องอัปเดตในแต่ละเนมสเปซที่เกิดขึ้นหากต้องการ

เมื่อโมดูลถูกโหลดใหม่พจนานุกรมของมัน (ที่มีตัวแปรส่วนกลางของโมดูล) จะถูกเก็บไว้ การกำหนดชื่อใหม่จะแทนที่คำจำกัดความเดิมดังนั้นโดยทั่วไปจะไม่มีปัญหา หากเวอร์ชันใหม่ของโมดูลไม่ได้กำหนดชื่อที่กำหนดโดยเวอร์ชันเก่านิยามเดิมจะยังคงอยู่

ดังนั้นโดยการสร้างกระบวนการใหม่และฆ่ากระบวนการเก่าคุณจะกำจัดการอ้างอิงที่ล้าสมัยทั้งหมด

นอกจากนี้ CLI ของ Flask ยังใช้โมดูล 'คลิก' ทำให้ง่ายต่อการเพิ่มคำสั่งที่กำหนดเอง แต่ที่สำคัญที่สุดคือนอกเหนือจากการแก้ไขข้อผิดพลาดในการโหลดซ้ำ CLI ยังเสนอวิธีมาตรฐานในการเรียกใช้แอปพลิเคชันและเพิ่มคำสั่งที่กำหนดเอง สิ่งนี้ฟังดูดีมากเพราะมันทำให้คุ้นเคยกับ Flask ที่สามารถถ่ายโอนระหว่างทีมและแอพพลิเคชั่นต่าง ๆ ได้ดีกว่าแทนที่จะมีหลายวิธีในการทำสิ่งเดียวกัน

ดูเหมือนว่าเป็นวิธีที่แท้จริงที่จะทำให้ Flask มากขึ้นตาม Zen of Python:

ควรมีอย่างใดอย่างหนึ่งและเด่นกว่าเพียงวิธีเดียวที่ชัดเจนที่จะทำ


2
นี่คือสิ่งที่ฉันคิดว่า Armin หมายถึง "การสนับสนุนที่ไม่ดี": ใน Python การโหลดโมดูลไม่ได้โหลดโมดูลที่โมดูลนั้นนำเข้าอีกครั้งและไม่เชื่อมโยงชื่อในโมดูลอื่นจากการชี้ไปยังวัตถุเก่าไปยังวัตถุใหม่จากโมดูลใหม่ ดังนั้นการแลกเปลี่ยนโมดูลใหม่เข้าสู่กระบวนการเดียวกันจึงเป็นปัญหา คุณดีกว่าการเริ่มต้นกระบวนการใหม่เมื่อคุณต้องการเปลี่ยนรหัส
Aaron Hall

ตอนนี้คุณพูดถึงมันแล้วฉันจำพฤติกรรมที่คุณอธิบายได้ขอบคุณสำหรับคำชี้แจง! ฉันจะแก้ไขคำตอบตามนั้น
Martin Jungblut Schreiner

ตกลงบวก 1 สำหรับการอ้างถึงฉัน :)
Aaron Hall

การเพิ่มเติมจาก Aaron Hall ชี้แจงให้ฉัน ขอบคุณ :)
Remco Haszing

7
ทำไมเราต้องใช้ตัวแปรสภาพแวดล้อมFLASK_APP? นั่นเป็นสิ่งที่แท้จริงในการทำงานของมันหรือไม่? ฉันอยากรู้ว่าทำไมflask runไม่ยอมรับเช่นเดียวกับการโต้แย้งซึ่งจะทำให้ผู้มาใหม่ง่ายขึ้น ขอบคุณ.
John Wheeler
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.