Initialize Flask-SocketIO python app on heroku - python

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?

Related

How to use only 'flask run' command in terminal and add default config variables in code instead?

I would like to start my Flask application with just flask run with these config informations: host = 0.0.0.0, port = 443 & cert = adhoc. If I wanted to run this through command I would have executed the code below instead:
flask run --host=0.0.0.0 --port=443 --cert=adhoc
But then I have to tell all my group mates and my professor to do it too when they check on my code. So I tried to work around this by using the code below in my app:
werkzeug.serving.run_simple("0.0.0.0", 443, app, ssl_context='adhoc')
However when I try to close my server with CTRL+C, it starts another server that actually would start if I didn't have any config informations. So I was wondering is there any way to go around this? Either it is to continue using werkzeug or to change it to something else.
Here is a shorten version of my code that includes how I have built my app. Tried to include what's just needed. If there's anything I should include more just tell me. Any help is appreciated. Thank you!
def create_app(test_config=None):
# create and configure the app
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='env',
DATABASE=os.path.join(app.instance_path, 'flaskr.sqlite'),
)
app.app_context().push()
if test_config is None:
# load the instance config, if it exists, when not testing
app.config.from_pyfile('config.py', silent=True)
else:
# load the test config if passed in
app.config.from_mapping(test_config)
# ensure the instance folder exists
try:
os.makedirs(app.instance_path)
except OSError:
pass
serving.run_simple("0.0.0.0", 443, app, ssl_context='adhoc')
return app
Environment variables would probably be the correct way to provide these values to the flask run command.
You never mentioned your platform, but on Linux, something like:
export FLASK_RUN_PORT=9999
Causes the dev sever to launch on port 9999 when launched with just flask run
You could also put these values in the file .flaskenv:
FLASK_RUN_PORT=9999
FLASK_RUN_HOST=0.0.0.0
flask run should then autoload these values.
By the way official documentation misses these values out, I had to look on issue 2661
Seems FLASK_RUN_CERT=adhoc is also a valid one, as per issue 3105.
Not sure if there's an extensive list of these anywhere.

wxPython and Flask integration

I am trying to integrate wxPython and Flask into a single application, but I am not sure how to get them to work together as they both want exclusive use of the main thread.
I am calling the application with:
export FLASK_APP=keypad_controller
python3 -m flask run -p 2020 -h 0.0.0.0 --eager-loading --no-reload
The main code block using Flask is:
from flask import Flask
def create_app(test_config=None):
app = Flask(__name__)
return app
I am not sure how to integrate wxPython (below) into the above code, how do I run flask?
wx_app = wx.App()
main_window = MainWindow(config)
main_window.Show()
wx_app.MainLoop()
Start Flask (app.run()) in a separate thread.
Not that if you want app.run(debug=True), you must also pass use_reloader=False, because things will go off the rails quickly if Flask decides that it wants needs to reload anything from other than the main thread.

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

Flask-Script add_option method not working

Using flask-script's add_option method I'm trying to pass the name of a config file into my create_app() so I can configure from_pyfile() -- Flask Instance Folders
I used this gist to get me started.
manage.py
from fbone import create_app
app = create_app()
manager = Manager(app)
manager.add_option('-c', '--config', dest='config', required=False)
app.py
def create_app(config=None, app_name=None, blueprints=None):
"""Create a Flask app."""
print config
This is just a snippet of my create_app function but I'm starting the app like this:
$ python manage.py -c config/heroku.cfg runserver
None
/env/lib/python2.7/site-packages/flask_script/__init__.py:153: UserWarning: Options will be ignored.
* Running on http://127.0.0.1:5000/
* Restarting with reloader
None
As you can see, instead of printing config/heroku.cfg it prints None
I think this is because of the UserWarning from flask script but I can't find out why that's happening.
It turns out you are creating the flask object by calling create_app() (with the parens).
If you do
app=create_app
or
Manager(create_app)
then you should be able to use
add_option()

Categories

Resources