Чому колбу CLI рекомендується використовувати на Flask.run?


13

У колбі 0,11 flaskбуло введено CLI. Як документи, так і стан змін, це рекомендується.

Документи сервера розробки :

Починаючи з Flask 0,11, існує кілька вбудованих способів запуску сервера розробки. Найкращий варіант - це утиліта командного рядка flask, але ви також можете продовжувати використовувати Flask.run()метод.

Командний рядок

Колба командного рядка сценарій (інтерфейс командного рядка) настійно рекомендується для розвитку , оскільки вона забезпечує чудовий досвід перезавантаження з - за того , як він завантажує додаток. Основне використання таке:

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

Журнал змін :

  • Додано flaskі flask.cliмодуль для запуску локального сервера налагодження через систему CLI клацання. Це рекомендується за старим flask.run()методом, оскільки він працює швидше і надійніше завдяки різній конструкції, а також замінює Flask-Script.

Поки що я не помітив цього "чудового досвіду перезавантаження". Я не бачу сенсу використання CLI для користувацького сценарію.

Якщо використовується Flask.run, я просто напишу файл python:

#!/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

Звичайно, це буде супроводжуватися додатковим сценарієм bat, як тільки користувачі 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 () та автоматичним перезавантаженням коду:

Це добре працює для загального випадку, але це не дуже добре для розвитку, тому рекомендується з колби 0,11 і далі метод колби. Причиною цього є те, що завдяки тому, як працює механізм перезавантаження, виникають деякі химерні побічні ефекти (наприклад, виконання певного коду двічі, іноді збої без повідомлення або відмирання, коли трапляється синтаксис чи імпорт).

Вони стверджують, що CLI не страждає від цієї проблеми.

Перше зобов’язання, яке, здається, стосується цього питання: https://github.com/pallets/flask/commit/3bdb90f06b9d3167320180d4a5055dcd949bf72f

І там Армін Ронахер написав:

Не рекомендується використовувати цю функцію для розробки з автоматичним перезавантаженням, оскільки це погано підтримується. Натомість вам слід скористатися підтримкою flaskсценарію командного рядка runserver.

Як згадував Аарон Холл, схоже, що використання run () може бути проблематичним через те, що всі об'єкти, які є екземплярами класів, визначених у модулях, що замінюються, не будуть повторно ініційовані, і кожного разу, коли модуль перезавантажується, модулі, які імпортують, також не перезавантажуються.

Детальну інформацію про це можна знайти в Python 3 за адресою: https://docs.python.org/3/library/importlib.html?highlight=importlib#module-importlib

У ньому зазначено:

Як і у всіх інших об'єктах Python, старі об'єкти відновлюються лише після того, як їх довідкова кількість опуститься до нуля.

Інші посилання на старі об'єкти (наприклад, імена, що знаходяться поза модулем) не відновлюються для посилання на нові об'єкти і повинні бути оновлені у кожному просторі імен, де вони виникають, якщо цього потрібно.

Коли модуль перезавантажується, його словник (містить глобальні змінні модуля) зберігається. Перевизначення імен замінить старі визначення, тому це, як правило, не є проблемою. Якщо нова версія модуля не визначає ім'я, яке було визначене старою версією, залишається старе визначення.

Отже, створивши новий процес і вбивши старий, ви, природно, усуваєте всі застарілі посилання.

Крім того, CLI Flask використовує модуль "click", що дозволяє легко додавати спеціальні команди, але головне, крім виправлення помилки для перезавантаження, CLI пропонує стандартизований спосіб запуску програм та додавання спеціальних команд. Це звучить як дуже гарна річ, адже це робить знайомство з Flask більш переданим між різними командами та програмами, а не наявністю кількох способів зробити те саме.

Це здається справжнім способом зробити Flask більше у відповідності до дзен Python:

Повинно бути один - і бажано лише один - очевидний спосіб це зробити.


2
Ось що я думаю, що Armin означає "погано підтримується": У Python перезавантаження модуля не перезавантажує модулі, які цей модуль імпортує, а також не пов'язує імена в інших модулях від вказівки на старі об'єкти до нових з нового модуля - тому гаряча заміна нового модуля на той самий процес є проблематичною. Вам набагато краще починати новий процес, коли ви хочете змінити код.
Аарон Холл

Тепер, коли ви це згадуєте, я пригадую описану вами поведінку, дякую за роз’яснення! Відповідь я відредагую відповідно.
Мартін Юнгблут Шрайнер

ок, плюс 1 за те, що мене цитують. :)
Аарон Холл

Додаток від Аарон Холл пояснив це для мене. Дякую. :)
Remco Haszing

7
Чому ми повинні використовувати змінну середовища FLASK_APP? Це суттєво, як це працює? Мені цікаво, чому flask runвін не сприймає те ж саме як аргумент, що полегшило б роботу бортів з новичками. Дякую.
Джон Вілер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.