Running Flask + Gunicorn + SocketIO - python

I am running SocketIOServer for my flask app and I have a run.py script which looks like this:
import sys
from idateproto import create_app, run_app
if __name__ == "__main__":
if len(sys.argv) > 1:
create_app(sys.argv[1])
else:
create_app('dev')
run_app()
and run_app looks like this:
def run_app():
# Run the application
# See flask/app.py run() for the implementation of run().
# See http://werkzeug.pocoo.org/docs/serving/ for the parameters of Werkzeug's run_simple()
# If the debug parameter is not set, Flask does not change app.debug, which is set from
# the DEBUG app config variable, which we've set in create_app().
#app.run(**app_run_args)
global app
global middleware
SocketIOServer((app.config['HOST'], app.conf`enter code here`ig['PORT']), middleware,namespace="socket.io", policy_server=False).serve_forever()
I run this like: python run.py production/dev/test etc.
Now I want to run it in production using gunicorn and all the tutorials online suggest doing something like:
gunicorn run:app -c gunicorn-config.py
My problem is I don't want to run just my app any more. I want to tell gunicorn to run the serve_forever method on the SocketIOServer instead, I have done a lot of research online and can not find a way to achieve this. Please help.

Related

How to run Uvicorn FastAPI server as a module from another Python file?

I want to run FastAPI server using Uvicorn from A different Python file.
uvicornmodule/main.py
import uvicorn
import webbrowser
from fastapi import FastAPI
from fastapi.responses import FileResponse
from fastapi.staticfiles import StaticFiles
app = FastAPI()
import os
script_dir = os.path.dirname(__file__)
st_abs_file_path = os.path.join(script_dir, "static/")
app.mount("/static", StaticFiles(directory=st_abs_file_path), name="static")
#app.get("/")
async def index():
return FileResponse('static/index.html', media_type='text/html')
def start_server():
# print('Starting Server...')
uvicorn.run(
"app",
host="0.0.0.0",
port=8765,
log_level="debug",
reload=True,
)
# webbrowser.open("http://127.0.0.1:8765")
if __name__ == "__main__":
start_server()
So, I want to run the FastAPI server from the below test.py file:
from uvicornmodule import main
main.start_server()
Then, I run python test.py.
But I am getting the below error:
RuntimeError:
An attempt has been made to start a new process before the
current process has finished its bootstrapping phase.
This probably means that you are not using fork to start your
child processes and you have forgotten to use the proper idiom
in the main module:
if __name__ == '__main__':
freeze_support()
...
The "freeze_support()" line can be omitted if the program
is not going to be frozen to produce an executable.
What I am doing wrong? I need to run this module as package.
When spawning new processes from the main process (as this is what happens when uvicorn.run() is called), it is important to protect the entry point to avoid recursive spawning of subprocesses, etc. As described in this article:
If the entry point was not protected with an if-statement idiom
checking for the top-level environment, then the script would execute
again directly, rather than run a new child process as expected.
Protecting the entry point ensures that the program is only started
once, that the tasks of the main process are only executed by the main
process and not the child processes.
Basically, your code that creates the new process must be under if __name__ == '__main__':. Hence:
from uvicornmodule import main
if __name__ == "__main__":
main.start_server()
Additionally, running uvicorn programmatically and having reload and/or workers flag(s) enabled, you must pass the application as an import string in the format of "<module>:<attribute>". For example:
# main.py
import uvicorn
from fastapi import FastAPI
app = FastAPI()
if __name__ == "__main__":
uvicorn.run("main:app", host="0.0.0.0", port=8000, reload=True)
As a sidenote, the below would also work, if reload and/or workers flags were not used:
if __name__ == "__main__":
uvicorn.run(app, host="0.0.0.0", port=8000)
Also, as per FastAPI documentation, when running the server from a terminal in the following way (the default port is 8000. Have a look at all the available command line options):
> uvicorn main:app --reload
the command uvicorn main:app refers to:
main: the file main.py (the Python "module").
app: the object created inside of main.py with the line app = FastAPI().
--reload: make the server restart after code changes. Only use for development.

Python Flask script auto-closes when run as .pyw file (workaround included)

Problem
I've created a basic python script, using flask to render an HTML page. On Windows 10, the script works perfectly as a *.py file, but when run as a *.pyw file, the page is not rendered.
In Task Manager, instances of python are opened and closed within seconds after running the script as *.pyw.
Code
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
def main():
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True, host="0.0.0.0", port=80)
Workaround
Run the *.py version with the following lines added to the code:
import ctypes
...
...
...
if __name__ == '__main__':
cytypes.windll.user32.ShowWindow(ctypes.windll.kernel32.GetConsoleWindow(), 0)
app.run(debug=True, host="0.0.0.0", port=80)
Code above hides the console, and starts the flask app successfully.
However, I am still interested in an explanation as to why the *.pyw method won't work, if anyone has an idea.
.pyw-files would ran on pythonw.exe rather than python.exe. The difference is, that pythonw.exe does not run in a console by default and runs asynchronous. This would mean that flask starts and runs in the background untill everything else terminates. Since you to not have anything else in your application, the programm ends directly.

Testing a sever launching cli application - Python, Flask

Question
How to test a blocking cli application in Python?
What I am trying to accomplish
I have a manage.py file, which is used to start the flask development web server (and other admin stuff that are out of the scope of this question). Typically I would run this as python manage.py runserver, but for keeping this question simple and relevant I have edited that file (see Minimal, Reproducible Example) to just run the server. So the below command works
$ python manage.py
* Serving Flask app "app" (lazy loading)
* Environment: production...
The issue comes with testing the functionality of this cli application. Click has the CliRunner.invoke(), which allows one to test cli applications. But in this particular case, it fails as the server is already running.
from manage import runserver
from click.testing import CliRunner
import requests
import unittest
class TestManage(unittest.TestCase):
def test_runserver(self):
runner = CliRunner()
runner.invoke(runserver)
# It blocks above, does not go below!
r = requests.get('http://127.0.0.1')
assert r.status_code == 200
if __name__ == '__main__':
unittest.main()
Attempts at solution
After trying many approaches I have found a "solution", which is to spawn a Process to start the server, run the tests and then terminating the process on exit (this is included in the example). This feels more like a hack rather than a "real" solution.
Minimal, Reproducible Example
Folder structure
flask_app/
- app.py
- manage.py
- test_manage.py
app.py
from flask import Flask
app = Flask(__name__)
#app.route('/')
def hello():
return 'Hello, World!'
manage.py
import click
from app import app
#click.command()
def runserver():
app.run()
if __name__ == "__main__":
runserver()
test_manage.py
from manage import runserver
from multiprocessing import Process
from click.testing import CliRunner
import requests
import unittest
class TestManage(unittest.TestCase):
def setUp(self):
self.runner = CliRunner()
self.server = Process(
target=self.runner.invoke,
args=(runserver, )
)
self.server.start()
def test_runserver(self):
r = requests.get('http://127.0.0.1')
assert r.status_code == 200
def tearDown(self):
self.server.terminate()
if __name__ == '__main__':
unittest.main()
And run the test using $ python test_manage.py.

Using custom signal handlers with gunicorn

I have a Flask application with custom signal handlers to take care of clean up tasks before exiting. When running the application with gunicorn, gunicorn kills the application before it can complete all clean up tasks.
You didn't explain what you mean by custom signal handlers, but I'm not sure that you should be using Flask's signals to capture process-level events, like shutdown. Instead, you can use the signal module from the standard library to hook onto the SIGTERM signal, like so:
# app.py - CREATE THIS FILE
from flask import Flask
from time import sleep, time
import signal
import sys
def create_app():
signal.signal(signal.SIGTERM, my_teardown_handler)
app = Flask(__name__)
#app.route('/')
def home():
return 'hi'
return app
def my_teardown_handler(signal, frame):
"""Sleeps for 3 seconds, then creates/updates a file named app-log.txt with the timestamp."""
sleep(3)
with open('app-log.txt', 'w') as f:
msg = ''.join(['The time is: ', str(time())])
f.write(msg)
sys.exit(0)
if __name__ == '__main__':
app = create_app()
app.run(port=8888)
# wsgi.py - CREATE THIS FILE, in same folder as app.py
import os
import sys
from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.exceptions import NotFound
from app import create_app
app = DispatcherMiddleware(create_app())
Assuming you have a virtual environment with Flask and Gunicorn installed, you should then be able to launch the app with Gunicorn:
$ gunicorn --bind 127.0.0.1:8888 --log-level debug wsgi:app
Next, in a separate terminal, you can send the TERM signal to your app, like so:
$ kill -s TERM [PROCESS ID OF GUNICORN PROCESS / $(ps ax | grep gunicorn | head -n 1 | awk '{print $1}')]
And to observe the results, you should notice that the contents of the app-log.txt file get updated when you run that kill command, after the three-second delay. You could even spawn a third terminal window in this directory and run watch -n 1 "cat app-log.txt" to observe this file being updated in real time, while you cycle between starting the app and sending the TERM signal.
As for tying that into production, I know that Supervisor has a configuration option to specify the stopsignal, like so:
[program:my-app]
command = /path/to/gunicorn [RUNTIME FLAGS]
stopsignal = TERM
...
But that's a separate topic from the original issue of ensuring that your app's clean up tasks are completely executed.

is there any way to run bottle application in daemon mode

I have a web application built on bottle(python) frame work and I want to run it in daemon mode.Is there any way to run it in daemon mode
Thanks
Sure you can. Install BottleDaemon 0.1.0 on your OS and than change your router file like so:
from bottledaemon import daemon_run
from bottle import route
#route("/hello")
def hello():
return "Hello World"
# The following lines will call the BottleDaemon script and launch a daemon in the background.
if __name__ == "__main__":
daemon_run()

Categories