Run code after flask application has started
Asked Answered
R

7

74

My goal is to get arbitrary code to run after my Flask application is started. Here is what I've got:

def run():
    from webapp import app
    app.run(debug=True, use_reloader=False)

Ideally I would be able to just do this:

def run():
    from webapp import app
    app.run(debug=True, use_reloader=False)
    some_code()

But the code doesn't continue past app.run(), so some_code() never runs.

The solution I'm working on at the moment is to run some_code() in a separate thread from app.run(), create a before first request function that sets this:

app.is_running = True

Then get some_code() to shoot a basic request to app so that the 'before first request' code runs. This is fairly convoluted and going to be hard to document. I would rather use app.is_running parameter which already is provided in Flask, or use a @app.after_server_start decorator, but to my knowledge neither of those exists.

Help me make this code better?


Posthumous: Every time I think about this issue, it makes me wish that a @app.after_server_start decorator existed.

Reyna answered 14/12, 2014 at 1:40 Comment(8)
Why not just use @app.before_first_request and be done with that. Why does the code need to run any earlier?Maurili
In other words, why is it so important that the code runs after starting the server (which you can't use like that in production code anyway, always use a proper WSGI container instead and not the flimsy-for-development-only Werkzeug server started with app.run()) but before a request has come in?Maurili
Mainly because it makes more sense to run the code after the server starts, but your right that it doesn't actually need to be run that earlyReyna
Either run the code at import time, or with before_first_request then.Maurili
Possibly answered by this post. Also "posthumous" not "post humus." Sorry. I had to.Mistymisunderstand
One way after-server-start makes more sense than before-first-request is when you're waiting for a log entry with version codes and you forget you need that first request and you think something ELSE is broken with log files and permissions and virtual environments AGAIN. Writing for a friend.Suiting
Another use case I came up against is where the app needs to make an asynchronous request for secret from an external source which is then posted back to one of the endpoints you're exposing. The endpoint doesn't exist until the app has started, but if you run it with before_first_request, it doesn't have time to complete.Edibles
I don't understand what's difficult to understand about a notification when the server is ready to accept requests. Sometimes you don't want to run a service 24/7 because it costs money. Sometimes the server you spin up is dynamically assigned an IP. A simple "I'm here" makes plenty of senseBelgae
R
40

If you need to execute some code after your flask application is started but strictly before the first request, not even be triggered by the execution of the first request as @app.before_first_request can handle, you should use Flask_Script, as CESCO said, but you could subclass the class Server and overwrite the __ call __ method, instead of overwriting the runserver command with @manager.command:

from flask import Flask
from flask_script import Manager, Server

def custom_call():
    #Your code
    pass

class CustomServer(Server):
    def __call__(self, app, *args, **kwargs):
        custom_call()
        #Hint: Here you could manipulate app
        return Server.__call__(self, app, *args, **kwargs)

app = Flask(__name__)
manager = Manager(app)

# Remeber to add the command to your Manager instance
manager.add_command('runserver', CustomServer())

if __name__ == "__main__":
    manager.run()

This way you don't override default options of runserver command.

Renovate answered 22/8, 2016 at 1:50 Comment(3)
I've one query that after deployment of my application will manager.run() continuously 24x7 or when an user hits the url then only ? I know it may look like a silly question but I want to knowCyclostome
this doesn't work for meSynecdoche
Flask_Script sadly doesn't work anymore with Flask since 2.0.1 and higher.Sacks
S
27

I just did (in a main.py executed with python main.py):

with app.app_context():
    from module import some_code
    some_code()

def run():
    from webapp import app
    app.run(debug=True, use_reloader=False)

This worked for me without the more comprehensive answers offered above. In my case some_code() is initializing a cache via flask_caching.Cache.

But it probably depends on what exactly some_code is doing...

Supertax answered 8/4, 2019 at 12:39 Comment(0)
C
23

I don't really like any of the methods mentioned above, for the fact that you don't need Flask-Script to do this, and not all projects are going to use Flask-Script already.

The easiest method, would be to build your own Flask sub-class. Where you construct your app with Flask(__name__), you would simply add your own class and use it instead.

def do_something():
  print('MyFlaskApp is starting up!')


class MyFlaskApp(Flask):
  def run(self, host=None, port=None, debug=None, load_dotenv=True, **options):
    if not self.debug or os.getenv('WERKZEUG_RUN_MAIN') == 'true':
      with self.app_context():
        do_something()
    super(MyFlaskApp, self).run(host=host, port=port, debug=debug, load_dotenv=load_dotenv, **options)


app = MyFlaskApp(__name__)
app.run()

Of course, this doesn't run after it starts, but right before run() is finally called. With app context, you should be able to do anything you may need to do with the database or anything else requiring app context. This should work with any server (uwsgi, gunicorn, etc.) as well.

If you need the do_something() to be non-blocking, you can simply thread it with threading.Thread(target=do_something).start() instead.

The conditional statement is to prevent the double call when using debug mode/reloader.

Confraternity answered 27/7, 2019 at 10:34 Comment(3)
In my case I needed only a backup process (worker) started by IIS and this approach worked (even leaving super(MyFlaskapp, self).run row out)Robbierobbin
To who is wondering: this WON'T work if you are using a WSGI server like werkzeug. Flask.run() is a debug entrypoint NOT called by werkzeug.Schwann
one more advice, better to use Thread(daemon=True) to make sure werkzeug is not stuck by non-daemon threads.Schwann
A
11

Use Flask-Script to run your app, then overwrite the runserver class/method like this

# manage.py

from flask_script import Manager

from myapp import app

manager = Manager(app)

def crazy_call():
    print("crazy_call")

@manager.command
def runserver():
    app.run()
    crazy_call()

if __name__ == "__main__":
    manager.run()
Ariannaarianne answered 15/11, 2015 at 16:56 Comment(2)
Thanks for your post. When our app is ran through uwsgi how do we integrate this manager?Eloign
It didn't work for me. I had to interchange the lines like following: app.run() and crazy_call().Grandfather
S
5

If you want to run a bunch of command (as a regular Py app) after flask run, use a multi processing library (for example multiprocessing), In this solution you can have an API/Web application beside of a system program.

import flask
from flask import request, jsonify,make_response
import time
import multiprocessing

app = flask.Flask('__name__')

def API(Conf):
   print('In API selction')
   app.run(host='0.0.0.0', port=1337,)
if __name__ == "__main__":
   config = {"Something":"SomethingElese"}
   p = multiprocessing.Process(target=API, args=(Conf,))
   p.start()
   #time.sleep(3)
   print('After Flask run')

Note: above code just a sample/idea. Maybe have some error. (But I used this solution in production area and it's fine.)

P.S. : (in my perspective) the problem of this solution: more difficulty in debugging section.

Sorgo answered 23/8, 2020 at 10:1 Comment(0)
A
5

I was using flask to test that a web service and client application were communicating intelligibly. (That is, as part of an automatic regression test to ensure that the plumbing of the web interface onto a backend utility was to protocol.)

I ran flask in a concurrent thread, like this:

import flask, requests, multiprocessing

app = flask.Flask(__name__)

@app.route("/"):
def frontpage():
    return "Hello World"

server = multiprocessing.Process(target=app.run)
try:
    server.start()

    # Is time.sleep(n) needed here to avoid a potential race condition?

    assert requests.get("http://127.0.0.1:5000/").text == "Hello World"

finally:
    server.terminate()
    server.join()

My actual case used OWSLib instead of requests, and flask was serving a WSGI application (PyWPS wrapping other code), which necessitated translating complex geometry objects between different interface layers. Nonetheless the basic problem was: to start the flask app (without blocking), to then run something that requires the live host, and tear down immediately afterward (without also publishing a shutdown button).

Agama answered 4/9, 2021 at 5:13 Comment(0)
I
3

I encountered this same issue in a flask app of mine. I wanted to start a scheduler at app startup, which would kick off some jobs at regular intervals. Since I deploy my apps inside docker containers, what I ended up doing is adding an "health check" endpoint which just returns a 200, and in my dockerfile configured that endpoint:

HEALTHCHECK CMD curl --fail http://localhost:8080/alive || exit 1

The default behavior is to execute that command every 30s, and the first run conveniently kicks off my init() method. https://docs.docker.com/engine/reference/builder/#healthcheck

Ipswich answered 2/11, 2018 at 17:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.