5

这是另一个烧瓶开发服务器重新加载器问题。有一百万个问题问为什么它会两次加载所有内容,而这不是其中之一。我知道它会加载所有内容两次,我的问题涉及处理这个现实,我还没有找到我认为解决我正在尝试做的事情的答案。

我的问题是,如何在退出时清理所有应用程序对象?

我目前的方法如下所示。在此示例中,我使用 atexit 函数运行我的清理代码。

from flask import Flask

app = Flask(__name__)
print("start_app_id: ", '{}'.format(id(app)))

import atexit
@atexit.register
def shutdown():
    print("AtExit_app_id: ", '{}'.format(id(app)))
    #do some cleanup on the app object here

if __name__ == "__main__":
    import os
    if os.environ.get('WERKZEUG_RUN_MAIN') == "true":
        print("reloaded_main_app_id: ", '{}'.format(id(app)))
    else:
        print("first_main_app_id: ", '{}'.format(id(app)))

    app.run(host='0.0.0.0', debug=True)

这段代码的输出如下:

start_app_id:  140521561348864
first_main_app_id:  140521561348864
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
start_app_id:  140105598483312
reloaded_main_app_id:  140105598483312
 * Debugger is active!
 * Debugger pin code: xxx-xxx-xxx
^CAtExit_app_id:  140521561348864

请注意,首次加载时,会创建一个 ID 为 '864 的应用程序对象。在自动重新加载期间,会创建一个 ID 为 '312 的新应用程序对象。然后,当我按下 Ctrl-C(最后一行)时,将调用 atexit 例程,原始的 '864 app 对象是可以使用 app 变量访问的对象,而不是较新的 '312 app 对象。

我希望能够在服务器关闭或 Ctrl-C 时(在本例中为 '864 和 '312)对浮动的所有应用程序对象进行清理。关于如何做到这一点的任何建议?

或者,如果我可以在重新加载后创建的较新的 '312 对象上运行清理,我也可以完成这项工作 - 但是我目前的方法只能让我清理原始应用程序对象。

谢谢。

UPDATE1:我发现了一个链接,建议使用 try/finally 而不是 atexit 挂钩来完成我上面打算做的事情。切换到此会导致与 atexit 完全相同的行为,因此对我的问题没有帮助:

from flask import Flask

app = Flask(__name__)
print("start_app_id: ", '{}'.format(id(app)))

if __name__ == "__main__":
    import os
    if os.environ.get('WERKZEUG_RUN_MAIN') == "true":
        print("reloaded_main_app_id: ", '{}'.format(id(app)))
    else:
        print("first_main_app_id: ", '{}'.format(id(app)))

    try:
        app.run(host='0.0.0.0', debug=True)
    finally:
        print("Finally_app_id: ", '{}'.format(id(app)))
        #do app cleanup code here
4

1 回答 1

4

在对 werkzeug 来源进行了一些挖掘之后,我找到了答案。答案是不可能做我想做的事——这是设计使然。

当使用烧瓶开发服务器(werkzeug)时,不可能在终止时清理所有现有的应用程序对象(例如 ctrl-C),因为 werkzeug 服务器捕获了键盘中断异常并“通过”它。您可以在 run_with_reloader 函数中 werkzeug 的 _reloader.py 的最后几行中看到这一点:

def run_with_reloader(main_func, extra_files=None, interval=1,
                      reloader_type='auto'):
    """Run the given function in an independent python interpreter."""
    import signal
    reloader = reloader_loops[reloader_type](extra_files, interval)
    signal.signal(signal.SIGTERM, lambda *args: sys.exit(0))
    try:
        if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
            t = threading.Thread(target=main_func, args=())
            t.setDaemon(True)
            t.start()
            reloader.run()
        else:
            sys.exit(reloader.restart_with_reloader())
    except KeyboardInterrupt:
        pass

如果您将上面的“除了 KeyboardInterrupt:”替换为“finally:”,然后运行原始问题中的第二个代码片段,您会发现创建的两个应用程序对象都已按需要进行了清理。有趣的是,在进行这些更改后,第一个代码片段(使用 @atexit)仍然无法正常工作。

所以总而言之,你可以在使用烧瓶开发服务器时清理所有现有的应用程序对象,但你需要修改 werkzeug 源来这样做。

于 2016-05-09T00:57:52.213 回答