Run flask application with uWSGI [duplicate] - python

This question already has answers here:
Run code after flask application has started
(7 answers)
Closed last year.
I have a flask application and I would like to run it in a "production" way using uwsgi.
I have my launcher.py:
from app import app
import db
if __name__ == "__main__":
db.init()
app.run()
If I run the application with simply python launcher.py it's all ok. Especially, the db.init() is called correctly.
However, if I run using uwgsi with uwsgi app.ini , db.init() is not called.
Here is app.ini:
[uwsgi]
wsgi-file = launcher.py
callable = app
socket = :8080
I'm new with flask and uwsgi so probably I missed something but I could not find the solution in the different tutorials I read.
Also, in case you need it to understand the project, here is app.py:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def index():
return "hello from flask"
if __name__ == "__main__":
db.init()
app.run()
All files are in the same level in my project:
webserver/
- app.ini
- launcher.py
- app.py
- db.py
So, what am I doing wrong here?

Your code under if __name__ == "__main__": is not executed because uwsgi does not run your script like python app.py. Instead it imports the module specified in wsgi-file and looks for an object specified as callable (app in our case). You can test it by using the following script:
from flask import Flask
app = Flask(__name__)
print(__name__)
if __name__ == '__main__':
app.run()
If you run it with python ./flask_app.py then the output will be
$ python ./flask_app.py
__main__
* Serving Flask app 'app' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
(notice __main__ - it means we're running the script). However if you run it with uwsgi the __name__ will be app (it will be the name of your file, so app in my case):
$ uwsgi --http 127.0.0.1:5000 --module app:app
...
*** Operational MODE: single process ***
app
WSGI app 0 (mountpoint='') ready in 0 seconds on interpreter 0x7fb2d0c067b0 pid: 46794 (default app)
*** uWSGI is running in multiple interpreter mode ***
spawned uWSGI worker 1 (and the only) (pid: 46794, cores: 1)
Similar output will be if you run your app with FLASK_APP=app flask run - it does not execute script, it just imports it and uses app object from it.
So, in order to initialize database you should either move your db initialization out of if __name__ == "__main__":
from flask import Flask
app = Flask(__name__)
class DB:
def init(self):
self.data = 'Hello, World'
db = DB()
#app.route("/")
def hello_world():
return db.data
db.init()
if __name__ == '__main__':
app.run()
Or add a before_first_request handler:
# replace db.init() with
#app.before_first_request
def init_db():
db.init()
Notice the difference between them - if you put db.init() as a top-level statement it will be executed once you load app.py. If you register a before_first_request callback it will be executed once first request arrives to your application. Pick the one that works best for you.

Related

Not getting web output

I used Flask command in my program for first time. Following was the bit of code I wrote:
from flask import Flask,jsonify, request
app = Flask(__name__)
#app.route("/")
def hello_world():
return "Hello World!"
if (__name__ == "__main__"):
app.run(debug=True)
This code was written by me in IDLE Shell 3.8-32 bit and the output should had come in a web browser. But it didn't came. I just got the following output from IDLE:
* Serving Flask app "sa" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
app.run(host=0.0.0.0, port=5000, debug=True)
You can change your port number to your convenience.

Python file runs using python command but not using Flask

I have a small application built using Flask and Flask-restx. I can run the app using python app.py and the flask server runs on port 8888. I try to run the file using set FLASK_APP=app.py and run using flask run which seems to run, but doesnt open my swagger page. Please advice.
app.py
import logging.config
import os
from flask import Flask, Blueprint
from flask_cors import CORS
from werkzeug.middleware.proxy_fix import ProxyFix
from src.config import default
from src.api.controllers.endpoints.users import ns as users_namespace
from src.api.controllers.endpoints.statuses import ns as status_namespace
from src.api import api
from src.database import db
app = Flask(__name__)
CORS(app)
app.wsgi_app = ProxyFix(app.wsgi_app)
logging_conf_path = os.path.normpath(os.path.join(os.path.dirname(__file__), '../logging.conf'))
logging.config.fileConfig(logging_conf_path)
log = logging.getLogger(__name__)
def configure_app(flask_app):
flask_app.config['SERVER_NAME'] = default.FLASK_SERVER_NAME
flask_app.config['SQLALCHEMY_DATABASE_URI'] = default.SQLALCHEMY_DATABASE_URI
flask_app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = default.SQLALCHEMY_TRACK_MODIFICATIONS
flask_app.config['SWAGGER_UI_DOC_EXPANSION'] = default.RESTPLUS_SWAGGER_UI_DOC_EXPANSION
flask_app.config['RESTPLUS_VALIDATE'] = default.RESTPLUS_VALIDATE
flask_app.config['RESTPLUS_MASK_SWAGGER'] = default.RESTPLUS_MASK_SWAGGER
flask_app.config['ERROR_404_HELP'] = default.RESTPLUS_ERROR_404_HELP
def initialize_app(flask_app):
configure_app(flask_app)
blueprint = Blueprint('CovidAPI', __name__, url_prefix='/')
api.init_app(blueprint)
api.add_namespace(users_namespace)
api.add_namespace(status_namespace)
flask_app.register_blueprint(blueprint)
db.init_app(flask_app)
def main():
initialize_app(app)
log.info('>>>>> Starting development server at http://{}/ <<<<<'.format(app.config['SERVER_NAME']))
app.run(debug=default.FLASK_DEBUG)
if __name__ == "__main__":
main()
Flask Settings:
# Flask settings
FLASK_SERVER_NAME = 'localhost:8888'
FLASK_DEBUG = True # Do not use debug mode in production
# Flask-Restplus settings
RESTPLUS_SWAGGER_UI_DOC_EXPANSION = 'list'
RESTPLUS_VALIDATE = True
RESTPLUS_MASK_SWAGGER = False
RESTPLUS_ERROR_404_HELP = False
Output using python command:
2020-04-29 10:25:42,519 - __main__ - INFO - >>>>> Starting development server at http://localhost:8888/ <<<<<
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
2020-04-29 10:25:42,610 - werkzeug - INFO - * Restarting with stat
2020-04-29 10:25:45,398 - __main__ - INFO - >>>>> Starting development server at http://localhost:8888/ <<<<<
2020-04-29 10:25:45,426 - werkzeug - WARNING - * Debugger is active!
2020-04-29 10:25:45,458 - werkzeug - INFO - * Debugger PIN: 258-749-652
2020-04-29 10:25:45,530 - werkzeug - INFO - * Running on http://localhost:8888/ (Press CTRL+C to quit)
Running through flask run:
* Serving Flask app "src\app.py" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: on
* Restarting with stat
* Debugger is active!
* Debugger PIN: 313-115-045
* Running on http://localhost:6000/ (Press CTRL+C to quit)
The flask run command works by importing an app object from your app.py module.
Therefor the stuff in this function is never executed in that case:
def main():
initialize_app(app)
log.info('>>>>> Starting development server at http://{}/ <<<<<'.format(app.config['SERVER_NAME']))
app.run(debug=default.FLASK_DEBUG)
When running with the python app.py command it is, because this gets executed:
if __name__ == "__main__":
main()
I'm not sure how it runs fully in your case with python, as neither the configure_app or initialize_app functions return the app object. However, you may wish to change the code to something like:
# ...
def configure_app(flask_app):
# ...
return flask_app
def initialize_app(flask_app):
# ...
return flask_app
app = initialize_app(app)
Now that (fully configured) app object is avaialable to flask run. You shouldn't need to set the FLASK_ENV environment variable, as it looks for an object called app by default.
If you still want the ability to run with the interpreter, then add this block to the end, although this is kinda defunct if using the flask command.
if __name__ == "__main__":
log.info('>>>>> Starting development server at http://{}/ <<<<<'.format(app.config['SERVER_NAME']))
app.run(debug=default.FLASK_DEBUG)

How do you customize the launching message for Flask?

I'm running a Flask app in Cloud9. Whenever I start my Flask app, it says this message:
* Serving Flask app "app" (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
Is there a way to change this message? I'd like it to say something like this:
Connect to me at http://0.0.0.0:80/!
I've searched stack overflow and the web but couldn't find anything. I'm starting my app with app.run().
Also, is it possible to make the URL cyan?
You can change everything besides Running on http://0.0.0.0:80/ (Press CTRL+C to quit) by changing show_server_banner of flask.cli:
from flask import Flask
import sys
cli = sys.modules['flask.cli']
# put your own message here
cli.show_server_banner = lambda *x: click.echo("My nice message")
app = Flask(__name__)
app.run(host='0.0.0.0', port='80')
To get rid of the Running on http://0.0.0.0:80/ ... message, you can use unittest.mock:
from unittest import mock
from werkzeug._internal import _log
def my_startup_log(*args):
# log all messages except for the * Running on message
if not args[1].startswith(" * Running on"):
return _log(*args)
app = Flask(__name__)
with mock.patch('werkzeug.serving._log') as mocked:
# patch the logger object and replace with own logger
mocked.side_effect = my_startup_logger
app.run(host='0.0.0.0', port='8000')
This is very hacky and depends on the internal implementation of flask. Be careful when using this in production code, as this could easily break.

unable to load configuration from uwsgi

I have below setup in my Python application
server.py
from bots.flask_app import app
from bots.flask_app.api import api
from bots.flask_app.public import public
from bots import db
from bots.commons.helpers.flask.json.serializer import make_alternative_encoder
from flask_debugtoolbar import DebugToolbarExtension
import logging
import bots.commons.managers.configuration as ConfigurationManager
logger = logging.getLogger()
#######
# Public functions
#######
def setup_db_and_app():
# Flask application bootstrap
config = ConfigurationManager.get_flask_rest_config()
app.config.update(config)
logger.debug('Flask configuration object: %s', app.config)
# MongoDB connection initialization
db.init_app(app)
# Debug toolbar enabled only if Flask in debug mode
if ConfigurationManager.get_raw_flask_rest_config()['flask']['debug']:
DebugToolbarExtension(app)
# Replace the serializer with the custom one (for ObjectId and DateTime serialization)
app.json_encoder = make_alternative_encoder(app.json_encoder)
# Register the components
app.register_blueprint(api)
app.register_blueprint(public)
def start_server():
setup_db_and_app()
logger.debug('Registered routes: %s', app.url_map)
app.run(host='0.0.0.0')
main.py
import bots.flask_app.server as FlaskApp
import bots.commons.managers.log as LogManager
# Logging initialization
LogManager.init_logging()
# Defined in server.py
FlaskApp.start_server()
I am trying to see whether this applicator can bed served by uwsgi as below
uwsgi --socket 0.0.0.0:8080 --protocol=http -w main
The output is as follows
INFO:werkzeug: * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
INFO:werkzeug: * Restarting with stat
unable to load configuration from uwsgi
My questions
1. Where can I find the configurations which are causing this issue?
2. Can main.py be defined as a callable and used as a parameter for -w?
This is an app which is already written by someone and I am trying make this application served through uwsgi.
Any suggestions would be helpful
Thanks
I had the 'unable to load configuration from uwsgi' error too. According to flask uwsgi docs:
Please make sure in advance that any app.run() calls you might have in your application file are inside an if __name__ == '__main__': block or moved to a separate file. Just make sure it’s not called because this will always start a local WSGI server which we do not want if we deploy that application to uWSGI.
I move app.run() to if __name__ == '__main__':, and the problem solved. Maybe you can try to put FlaskApp.start_server() under if __name__ == '__main__':.
I'm a bit late to the party, I've encountered this error when I forgot to remove debug=True from app.run(). It makes sense that you can't run the debug server with uwsgi.

Debugging a Flask app running in Gunicorn

I've been working on a new dev platform using nginx/gunicorn and Flask for my application.
Ops-wise, everything works fine - the issue I'm having is with debugging the Flask layer. When there's an error in my code, I just get a straight 500 error returned to the browser and nothing shows up on the console or in my logs.
I've tried many different configs/options.. I guess I must be missing something obvious.
My gunicorn.conf:
import os
bind = '127.0.0.1:8002'
workers = 3
backlog = 2048
worker_class = "sync"
debug = True
proc_name = 'gunicorn.proc'
pidfile = '/tmp/gunicorn.pid'
logfile = '/var/log/gunicorn/debug.log'
loglevel = 'debug'
An example of some Flask code that borks- testserver.py:
from flask import Flask
from flask import render_template_string
from werkzeug.contrib.fixers import ProxyFix
app = Flask(__name__)
#app.route('/')
def index():
n = 1/0
return "DIV/0 worked!"
And finally, the command to run the flask app in gunicorn:
gunicorn -c gunicorn.conf.py testserver:app
Thanks y'all
The accepted solution doesn't work for me.
Gunicorn is a pre-forking environment and apparently the Flask debugger doesn't work in a forking environment.
Attention
Even though the interactive debugger does not work in
forking environments (which makes it nearly impossible to use on
production servers) [...]
Even if you set app.debug = True, you will still only get an empty page with the message Internal Server Error if you run with gunicorn testserver:app. The best you can do with gunicorn is to run it with gunicorn --debug testserver:app. That gives you the trace in addition to the Internal Server Error message. However, this is just the same text trace that you see in the terminal and not the Flask debugger.
Adding the if __name__ ... section to the testserver.py and running python testserver.py to start the server in development gets you the Flask debugger. In other words, don't use gunicorn in development if you want the Flask debugger.
app = Flask(__name__)
app.config['DEBUG'] = True
if __name__ == '__main__':
app.run()
## Tip for Heroku users:
Personally I still like to use `foreman start`, instead of `python testserver.py` since [it sets up all the env variables for me](https://devcenter.heroku.com/articles/config-vars#using-foreman). To get this to work:
Contents of Procfile
web: bin/web
Contents of bin/web, file is relative to project root
#!/bin/sh
if [ "$FLASK_ENV" == "development" ]; then
python app.py
else
gunicorn app:app -w 3
fi
In development, create a .env file relative to project root with the following contents (docs here)
FLASK_ENV=development
DEBUG=True
Also, don't forget to change the app.config['DEBUG']... line in testserver.py to something that won't run Flask in debug mode in production.
app.config['DEBUG'] = os.environ.get('DEBUG', False)
The Flask config is entirely separate from gunicorn's. Following the Flask documentation on config files, a good solution would be change my source to this:
app = Flask(__name__)
app.config.from_pyfile('config.py')
And in config.py:
DEBUG = True
For Heroku users, there is a simpler solution than creating a bin/web script like suggested by Nick.
Instead of foreman start, just use foreman run python app.py if you want to debug your application in development.
I had similiar problem when running flask under gunicorn I didn't see stacktraces in browser (had to look at logs every time). Setting DEBUG, FLASK_DEBUG, or anything mentioned on this page didn't work. Finally I did this:
app = Flask(__name__)
app.config.from_object(settings_map[environment])
if environment == 'development':
from werkzeug.debug import DebuggedApplication
app_runtime = DebuggedApplication(app, evalex=False)
else:
app_runtime = app
Note evalex is disabled because interactive debbugging won't work with forking (gunicorn).
I used this:
gunicorn "swagger_server.__main__:app" -w 4 -b 0.0.0.0:8080
You cannot really run it with gunicorn and for example use the flask reload option upon code changes.
I've used following snippets in my api launchpoint:
app = Flask(__name__)
try:
if os.environ["yourapp_environment"] == "local":
run_as_local = True
# some other local configs e.g. paths
app.logger.info('Running server in local development mode!')
except KeyError as err:
if "yourapp_environment" in err.args:
run_as_local = False
# some other production configs e.g. paths
app.logger.info('No "yourapp_environment env" given so app running server in production mode!')
else:
raise
...
...
...
if __name__ == '__main__':
if run_as_local:
app.run(host='127.0.0.1', port='8058', debug=True)
else:
app.run(host='0.0.0.0')
For above solution you need to give export yourapp_environment = "local" in the console.
now I can run my local as python api.py and prod gunicorn --bind 0.0.0.0:8058 api:app
The else statement app.run() is not actually needed, but I keep it for reminding me about host, port etc.
Try setting the debug flag on the run command like so
gunicorn -c gunicorn.conf.py --debug testserver:app
and keep the DEBUG = True in your Flask application. There must be a reason why your debug option is not being applied from the config file but for now the above note should get you going.

Categories