0

I have a simple flask app that I want to restart if the server port is changed in a config file. I have the following code:

#!/usr/bin/env python

from flask import Flask, jsonify
import sys, time, os

app = Flask(__name__,static_url_path='')

HTTPPort = 8080

#------------------------------------------------------------
@app.route("/cmd/<command>")
def command(command):

    if command == "restart":
        Reload()
    return jsonify("OK")

#------------------------------------------------------------
def Reload():
    # reload python script
    os.execl(sys.executable, 'python', __file__, *sys.argv[1:])

#------------------------------------------------------------
if __name__ == "__main__":

    # load options from file, which change HTTPPort value
    # LoadConfig()
    while True:
        try:
            app.run(host="0.0.0.0", port=HTTPPort)
        except Exception as e1:
            print("Error in app.run:" + str(e1))
            time.sleep(2)
            Reload()

The problem is that when os.execl is called it reloads the python script but app.run fails with "[Errno 98] Address already in use"

Once the app is loaded you can trigger the command routine in a browser by entering in the address windows of the browser :8080/cmd/restart . This will cause the flask app to accept the "restart" command, which will cause os.exec() to be called. At this point the app will terminate and reload. On the reload app.run() will error out with "Address already in use" via the exception and it will try to reload every 2 seconds, same error every time.

I tried setting reload= True in the app.run argument list however this would not allow the port to be changed. Is this possible to accomplish or am I doing something wrong?

Thanks

2
  • So, you are re-running app.run() in the while loop and spawning a new process? Commented Mar 10, 2018 at 3:59
  • No, app.run will not return in normal situations for a Flask app. app.run will stop executing when os.execl is executed. Once the app is loaded you can trigger the command routine in a browser by entering in the address windows of the browser <IPADDRESS>:8080/cmd/restart . This will cause the flask app to accept the "restart" command, which will cause os.exec() to be called. At this point the app will terminate and relaod. On the reload app.run() will error out with "Address already in use" via the exception and it will try to reload every 2 seconds, same error every time. Commented Mar 10, 2018 at 4:44

1 Answer 1

1

Totally different answer after experimenting myself. I think you might have to do a two part process.

First save your desired port number, seconds update the timestamp on your file so the built in reloader re-starts the application for you.

Combine that with a redirect to your new address and the operation is almost seamless. This doesn't check if the port number is in use so you might be dead if you pick an existing port, but otherwise the code is pretty straight forward.

#!/usr/bin/env python

from flask import Flask, redirect
import os
import configparser

config = configparser.ConfigParser()
config.read('portnumber.ini')

app = Flask(__name__,static_url_path='')

HTTPPort = config['MAIN'].getint('portnumber')


def touchMe():
    with open(__file__, 'a'):
        print(" - setting timestamp of " + __file__ )
        os.utime(__file__, None)


@app.route("/newport/<newport>")
def changeRoute(newport):
    HTTPPort = newport
    name = newport
    config['MAIN']['portnumber'] = newport
    with open('portnumber.ini', 'w') as configfile:
        config.write(configfile)
    touchMe()
    newurl = "http://localhost:" + HTTPPort + "/"
    print("Redirecting to " + newurl)

    return redirect(newurl, code=302)


#------------------------------------------------------------
if __name__ == "__main__":

    # load options from file, which change HTTPPort value
    # LoadConfig()
    while True:
        app.run(host="0.0.0.0", port=HTTPPort, debug=True, use_reloader=True)
Sign up to request clarification or add additional context in comments.

7 Comments

Actually, this is what the actual code is doing. I commented out my LoadConfig() function. It uses configparser. I removed this for simplicity. When I do reload, configparser does change the port. The problem I have is that if I use os.execl to restart, Flask does not release the port. If I restart from the command line, it works by killing the app and restarting. If I put this into a script (kill and restart) and call the script from python via os.system or subprocess.call, I still get the error listed above (Address already in use)
I have this working for me and updated my answer. My trick was to use the built in reloader and just trigger it by updating the timestamp on the file rather than any exec process trickery.
What version of Flask are you using? I am using 0.12.2. When I run your example, the config file is updated, the redirect happens, but the port is still the same. This approach you are taking is what I first started with: use_reloader and just "touch" the file to update the date to force an update. For me, this works for all other code in the program, but any variables passed into app.run() are not updated. The app does not respond to the new ports. Also, I am running this on the command line "python FlaskTest.py". I am not using any other Flask cli interfaces.
I have a workaround but I am still curious why this is not working. The workaround is to create a script that will kill the program (via pkill) and restart the program. I call this script via subprocess.call() in the Reload() function. Sometimes it fails on the first attempt and the exception in main() will call Reload() again and it works on the seconds attempt. Not really and ideal solution but it does work.
Interesting, it works on Windows 10. I tested with python 3.6.4 and it works. I took your example and tested it on both Windows and Linux. Works on Windows, not on Linux. I did some digging in the github repo for Flask, it looks like there are some limitations to the reloader on some platforms. I will ask in that forum. If I find out anything definitive I will post it here as well. Thanks for you help!!
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.