Brian M. Hunt
Brian M. Hunt

Reputation: 83828

Hook when Flask restarts in debug mode

While using Flask/Werkzeug in its debug mode, I am opening a number of files in threads (with watchdog) that I'd like to close because otherwise I eventually start to get OSError: too many files. I'd like to run a hook before Flask/Werkzeug shuts down in debug mode to do so (with, incidentally, observer.stop() and observer.join() from the Watchdog docs).

It isn't clear from the documentation or source code where it may be possible to do that.

It looks like the restart is happening around the function run_with_reloader in Werkzeug/serving.py:523. Seems there isn't any way to hook in here through an exposed API.

What would be the best way to call some code to reap threads before the process is restarted?

Upvotes: 5

Views: 1074

Answers (1)

Ôrel
Ôrel

Reputation: 7632

I think I have found a way to handle the issue. The issue comes from the first werkzeug reload saying which watcher for file it will used, logging something like:

* Restarting with inotify reloader"

The reload is done with a subprocess.call, so we have 2 different Python interpreters running at the same time!

On the other reloads (on file changed), there is a good advantage, the subprocess is killed before launching a new one, quite brutal but doing the job:

Here how it is done on werkzeug:

        sig = getattr(signal, "SIGKILL", signal.SIGTERM)
        # reloader active
        if is_running_from_reloader():
            os.kill(os.getpid(), sig)

My solution is, when I am using werkzeug (when app.debug is true in my case) and only do my background thread init on the reloaded subprocess. To know your are in the subprocess werkzeug provides is_running_from_reloader function

into app/__init__.py:

if not app.debug or app.debug and werkzeug.serving.is_running_from_reloader():
    # do what you want to do at start up

and somewhere else, for me into a class, do cleaning at exit (when process is killed)

class MyClass:
    @classmethod
    def cleanOnExit(cls):
        # do here your cleaning
import atexit ; atexit.register(MyClass.cleanOnExit)

Upvotes: 3

Related Questions