Gunicorn does not execute statements within main - python

from flask import Flask, request, jsonify
app = Flask(__name__)
#app.route('/api', methods=['POST'])
def predict():
pass
#Some statements to predict something
if __name__ == '__main__':
print("Hello World")
app.run(host='0.0.0.0', debug=True)
When I run as
gunicorn -b 0.0.0.0 app:app I do not see the print statement. However when I run as python app.py, the "Hello World" gets printed. The app runs but it does not execute the print statement. Any idea what causes the Gunicorn to ignore what is within main()?

When you run as gunicorn -b 0.0.0.0 app:app, gunicorn will only import the app from your app.py file. It will skip statements from the if __name__ == '__main__' block and hence you're not seeing the output from the print statement.
But when you run as python app.py, if __name__ == '__main__' block will be the entry point and hence print statement gets executed.
Also note that, the app.run(host='0.0.0.0', debug=True) line in if __name__ == '__main__' will start the development server and not the gunicorn server.
Attaching the link here for the similar question:
code before app.run() can not be run in gunicorn+flask

Related

Initialize Flask-SocketIO python app on heroku

I'm new to python, so this might be a dumb question, but I have the following issue:
I'm trying to deploy a Flask-SocketIO app to heroku, my app.py looks like this:
app = Flask(__name__)
socketio = SocketIO(app)
opt: Dict[Any, Any] = {}
.
.
#socketio.on('connect')
def joined():
test = json.dumps(opt)
emit('test', test)
.
.
if __name__ == '__main__':
opt = setup_args()
socketio.run(app)
My procfile looks like this:
web: gunicorn -k flask_sockets.worker app:app
If i run heroku local my server starts as expected, and I can establish a socket conection with my client, but my variable opt seems not to be filled. From what i've read in the docs, this is because the procfile does the socketio.run(app) for me, and my __main__ part is not getting executed.
I need to somehow trigger a method that initializes some variables in my app.py.
How can I achieve this?
Thanks
Is there a reason why you don't simply move opt = setup_args() out of the if statment, and move it, say, somewhere at the top of the file?

Run Flask Restplus APIs through Gunicorn

I have a sample Flask-RestPlus application as myapp.py:
from flask import Flask
from flask_restplus import Resource, API
API = Api(version='1.0', title='my_routes')
MY_NAMESPACE = API.namespace('v1.0/routes', description='v1.0 routes')
def initialize():
app = Flask(__name__)
API.init_app(app)
API.add_namespace(MY_NAMESPACE)
return app
#MY_NAMESPACE.route('/hello')
class HelloWorld(Resource):
def get(self):
return {'hello': 'world'}
if __name__ == '__main__':
MY_ROUTE = initialize()
MY_ROUTE.run(host='0.0.0.0', port=8080)
This works fine if I am running python myapp.py. Since I want to run to run this code on production, I want to use Gunicorn. I have gunicorn installed on my UNIX machine. Sample code from https://gunicorn.org/ works perfectly fine. How do I run code in my case? I tried:
gunicorn -w 4 myapp:initialize
gunicorn -w 4 "myapp:initialize()"
gunicorn -w 4 myapp:main
But none of them worked. Do I have to make some changes in code?
In your myapp.py, turn your part:
if __name__ == '__main__':
MY_ROUTE = initialize()
MY_ROUTE.run(host='0.0.0.0', port=8080)
into:
app = initialize() # app has to be accessible outside if
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8080)
and then run
gunicorn --bind 0.0.0.0:8080 --workers 4 myapp:app --log-level debug
if you want to run in production mode, specify --log-level info.

Flask application exits after returning exit code 0 [duplicate]

This question already has answers here:
What does if __name__ == "__main__": do?
(45 answers)
Closed 3 years ago.
I'm writing a basic "Hello world" program in Flask which exits without giving any output. The following is my code:
import flask
app = flask.Flask(__name__)
#app.route("/")
def hello():
return "Hello World"
if __name__ == "main":
app.run(debug=True, host='0.0.0.0')
print("test")
I'm getting the following output:
Process finished with exit code 0
But my http server is not starting, I get the following output:
$ curl -I http://127.0.0.1:5000
curl: (7) Failed to connect to 127.0.0.1 port 5000: Connection refused
When a python file is executed, the value of __name__ is "__main__" and not "main". Your code isn't entering into the if statement, which is why the Flask server never starts.
import flask
app = flask.Flask(__name__)
#app.route("/")
def hello():
return "Hello World"
if __name__ == "__main__": # There is an error on this line
app.run(debug=True, host='0.0.0.0')
print("test")

I don't need the run() method in a Flask application?

I have a Flask application setup on my linode with a directory structure like so:
|--------flask-test
|----------------app
|-----------------------static
|-----------------------templates
|-----------------------venv
|-----------------------__init__.py
|-----------------------main.py
my __init__.py is:
# __init__.py
from flask import Flask
from main import main
app = Flask(__name__)
app.register_blueprint(main)
app.run()
and main.py like so:
# main.py
from flask import Blueprint
main = Blueprint('main',__name__)
#main.route("/")
def hello():
return "Hello World!"
#main.route("/england/")
def england():
return "Hello England!"
If I run the app locally there are no issues. If I go to my server address in the web browser I get an internal server error. However if I remove the line: app.run from __init__.py it works fine. Why is this? Why do I not need the run method?
You should do
if __name__ == '__main__':
app.run()
The reason is that Apache or NGINX or some other web server loads your app directly on the server but app.run() runs flask's internal web-server so you can test your app.
It is a bit odd to have app.run() inside of the __init__.py file, normally it would be in a separate application script you run, where it would be written as:
if __name__ == '__main__':
app.run()
This way app.run() is only called when that script is executed as the application.
This is necessary because you do not want app.run() called when hosting under a WSGI server such as mod_wsgi or gunicorn. When using such WSGI servers, even if reusing the same script file as holder of the WSGI application entrypoint, __name__ wouldn't be set to __main__ but the basename of the script file. This ensures that app.run() isn't called where it is the separate WSGI server which is running the web server component.

Why is Flask application not creating any logs when hosted by Gunicorn?

I'm trying to add logging to a web application which uses Flask.
When hosted using the built-in server (i.e. python3 server.py), logging works. When hosted using Gunicorn, the log file is not created.
The simplest code which reproduces the problem is this one:
#!/usr/bin/env python
import logging
from flask import Flask
flaskApp = Flask(__name__)
#flaskApp.route('/')
def index():
flaskApp.logger.info('Log message')
print('Direct output')
return 'Hello World\n'
if __name__ == "__main__":
logHandler = logging.FileHandler('/var/log/demo/app.log')
logHandler.setLevel(logging.INFO)
flaskApp.logger.addHandler(logHandler)
flaskApp.logger.setLevel(logging.INFO)
flaskApp.run()
The application is called using:
gunicorn server:flaskApp -b :80 -w 4
--access-gfile /var/log/demo/access.log
--error-logfile /var/log/demo/error.log
When doing a request to the home page of the site, the following happens:
I receive the expected HTTP 200 "Hello World\n" in response.
There is a trace of the request in /var/log/demo/access.log.
/var/log/demo/error.log stays the same (there are just the boot events).
There is the "Direct output" line in the terminal.
There is no '/var/log/demo/app.log'. If I create the file prior to launching the application, the file is not modified.
Note that:
The directory /var/log/demo can be accessed (read, write, execute) by everyone, so this is not the permissions issue.
If I add StreamHandler as a second handler, there is still no trace of the "Log message" message neither in the terminal, nor in Gunicorn log files.
Gunicorn is installed using pip3 install gunicorn, so there shouldn't be any mismatch with Python versions.
What's happening?
This approach works for me: Import the Python logging module and add gunicorn's error handlers to it. Then your logger will log into the gunicorn error log file:
import logging
app = Flask(__name__)
gunicorn_error_logger = logging.getLogger('gunicorn.error')
app.logger.handlers.extend(gunicorn_error_logger.handlers)
app.logger.setLevel(logging.DEBUG)
app.logger.debug('this will show in the log')
My Gunicorn startup script is configured to output log entries to a file like so:
gunicorn main:app \
--workers 4 \
--bind 0.0.0.0:9000 \
--log-file /app/logs/gunicorn.log \
--log-level DEBUG \
--reload
When you use python3 server.py you are running the server3.py script.
When you use gunicorn server:flaskApp ... you are running the gunicorn startup script which then imports the module server and looks for the variable flaskApp in that module.
Since server.py is being imported the __name__ var will contain "server", not "__main__" and therefore you log handler setup code is not being run.
You could simply move the log handler setup code outside of the if __name__ == "__main__": stanza. But ensure that you keep flaskApp.run() in there since you do not want that to be run when gunicorn imports server.
More about what does if __name__ == “__main__”: do?
There are a couple of reasons behind this: Gunicorn has its own loggers, and it’s controlling log level through that mechanism. A fix for this would be to add app.logger.setLevel(logging.DEBUG).
But what’s the problem with this approach? Well, first off, that’s hard-coded into the application itself. Yes, we could refactor that out into an environment variable, but then we have two different log levels: one for the Flask application, but a totally separate one for Gunicorn, which is set through the --log-level parameter (values like “debug”, “info”, “warning”, “error”, and “critical”).
A great solution to solve this problem is the following snippet:
import logging
from flask import Flask, jsonify
app = Flask(__name__)
#app.route('/')
def default_route():
"""Default route"""
app.logger.debug('this is a DEBUG message')
app.logger.info('this is an INFO message')
app.logger.warning('this is a WARNING message')
app.logger.error('this is an ERROR message')
app.logger.critical('this is a CRITICAL message')
return jsonify('hello world')
if __name__ == '__main__':
app.run(host=0.0.0.0, port=8000, debug=True)
else:
gunicorn_logger = logging.getLogger('gunicorn.error')
app.logger.handlers = gunicorn_logger.handlers
app.logger.setLevel(gunicorn_logger.level)
Refrence: Code and Explanation is taken from here

Categories

Resources