Search code examples
pythonflaskflask-restplus

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)

Solution

  • 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)