4

I have the following script which just boots up a web server serving a dynamically created website. In order to get dynamic data the script opens a file to read the data.

My concern is how can I catch CTRL-C command for killing the python script so I can close the file before script thread is killed.

I tried the following couple things but neither work:

from flask import Flask, render_template
import time

# Initialize the Flask application
app = Flask(__name__)

fileNames = {}
fileDesc = {}
for idx in range(1,4):
    fileNames["name{}".format(idx)] = "./name" + str(idx) + ".txt"
    fileDesc["name{}".format(idx)] = open(fileNames["name{}".format(idx)],'r')

try:
    @app.route('/')
    def index():
        # code for reading data from files
        return render_template('index.html', var1 = var1)

    @app.errorhandler(Exception)
    def all_exception_handler(error):
        print("Closing")
        for key, value in fileDesc:
            val.close()
        print("Files closed")


    if __name__ == '__main__':
        app.run(
            host="192.168.0.166",
            port=int("8080"),
            debug=True
        )

except KeyboardInterrupt:
    print("Closing")
    for key, value in fileDesc:
        val.close()
    print("Files closed")

Thanks in advance.

theme
  • 1,097
  • 2
  • 9
  • 13
  • `KeyboardInterrupt`s don't work as expected in python2. What python are you on? – cs95 Jul 02 '17 at 14:06
  • You could also try playing around with signal. Read [this](https://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python) – idjaw Jul 02 '17 at 14:09
  • @cᴏʟᴅsᴘᴇᴇᴅ I'm using Python3. – theme Jul 03 '17 at 14:05
  • Odd. It should work. You could try signal handlers as @idjaw mentioned. – cs95 Jul 03 '17 at 14:06
  • @idjaw I'll check your link. But In python for local application it is very simple to do it and I've been successful, but with Flask it is more complicated. Don't know if it is because it halts thread or what. But I'll check signal handlers as well – theme Jul 03 '17 at 14:07

3 Answers3

3

I am struggling with the same thing in my project. Something that did work for me was using signal to capture CTRL-C.

import sys
import signal
def handler(signal, frame):
  print('CTRL-C pressed!')
  sys.exit(0)
signal.signal(signal.SIGINT, handler)
signal.pause()

When this piece of code is put in the script that is running the Flask app, the CTRL-C can be captured. As of now, you have to use CTRL-C twice and then the handler is executed though. I'll investigate further and edit the answer if I find something new.

Edit 1

Okay I've done some more research and came up with some other methods, as the above is quite hack 'n slash.

In production, clean-up code such as closing databases or files is done via the @app.teardown_appcontext decorator. See this part of the tutorial.

When using the simple server, you can shut it down via exposing the werkzeug shutdown function. See this post.

Edit 2

I've tested the Werkzeug shutdown function, and it also works together with the teardown_appcontext functions. So I suggest to write your teardown functions using the decorator and writing a simple function that just does the shutdown of the werkzeug server. That way production and development code are the same.

brvh
  • 264
  • 3
  • 15
  • 3
    Could you elaborate more on the solution? I am also interested in this kind of code, as I am running an application with flask plus socketio and background processes which need to be shut down when flask closes – Arturo Ribes Nov 08 '18 at 15:26
  • from: [this question](https://stackoverflow.com/questions/1112343/how-do-i-capture-sigint-in-python?answertab=votes#tab-top) this is not a viable solution for windows – MjBVala Jan 11 '21 at 05:49
  • 1
    `signal.pause()` what this line is used for? – Abd-Elaziz Sharaf Mar 17 '21 at 13:02
  • 1
    It causes the signal process to sleep until a signal is received (in this case the kill signal) https://docs.python.org/3/library/signal.html#signal.pause Otherwise you'd be sleeping and blocking the process. – brvh Mar 21 '21 at 11:10
  • I find the name misleading, therefore I feel it's worth mentioning that `teardown_appcontext` runs at the end of every request. Not at the end of a Flask application runtime. It follows that you might want to use it to handle resources between requests. But maybe not for terminating all connections to a database. https://flask.palletsprojects.com/en/2.2.x/appcontext/#storing-data – psq Dec 09 '22 at 09:08
0

Use atexit to handle this, from: https://stackoverflow.com/a/30739397/5782985

import atexit

#defining function to run on shutdown
def close_running_threads():
    for thread in the_threads:
        thread.join()
    print "Threads complete, ready to finish"

#Register the function to be called on exit
atexit.register(close_running_threads)

#start your process
app.run()
Galley
  • 493
  • 6
  • 9
0

I use the VScode to run flask app and I run into the same thing as you. In the "OUTPUT" below the VScode, "press CTRL+C to quit", but actuall it doesn't work, so as the CTRL+SPACE. My solution is to use CTRL+ALT+M instead. Provided that CTRL+C is working on linux terminal, I consider that CTRL+C should be helpful in interactive terminal like CMD,PowerShell or bash. The "OUTPUT" area of VScode may only be a place to output info.

HONOR G
  • 1
  • 1