I want my Flask app to have different behaviors when it is being run on localhost and when it is being hosted online. How can I detect from a flask app when it is on localhost and when it is deployed?
You'll want to look at the configuration handling section of the docs, most specifically, the part on dev / production. To summarize here, what you want to do is:
Load a base configuration which you keep in source control with sensible defaults for things which need to have some value. Anything which needs a value should have the value set to what makes sense for production not for development.
Load an additional configuration from a path discovered via an environment variable that provides environment-specific settings (e. g. the database URL).
An example in code:
from __future__ import absolute_imports
from flask import Flask
import .config # This is our default configuration
app = Flask(__name__)
# First, set the default configuration
app.config.from_object(config)
# Then, load the environment-specific information
app.config.from_envvar("MYAPP_CONFIG_PATH")
# Setup routes and then ...
if __name__ == "__main__":
app.run()
See also: The docs for Flask.config
Here is one way of doing it. The key is comparing the current root url flask.request.url_root against a known url value you want to match.
Excerpt taken from github repo https://github.com/nueverest/vue_flask
from flask import Flask, request
def is_production():
""" Determines if app is running on the production server or not.
Get Current URI.
Extract root location.
Compare root location against developer server value 127.0.0.1:5000.
:return: (bool) True if code is running on the production server, and False otherwise.
"""
root_url = request.url_root
developer_url = 'http://127.0.0.1:5000/'
return root_url != developer_url
Related
I have a Flask API and I am attempting to implement logging. Everything is working fine in my local machine, but nothing seems to work when deployed on AWS Elastic Beanstalk.
I have a file logger.py that creates and configures the logger that looks like this:
import logging
import os
if not os.path.isdir(os.environ.get('LOG_PATH', 'log')):
os.mkdir(os.environ.get('LOG_PATH', 'log'))
# Configure logger
logger = logging.getLogger(__name__)
formatter = logging.Formatter('[%(asctime)s] %(levelname)s in %(module)s: %(message)s')
debug_file_handler = logging.FileHandler(f'{os.environ.get("LOG_PATH", "log")}/debug.log')
info_file_handler = logging.FileHandler(f'{os.environ.get("LOG_PATH", "log")}/info.log')
error_file_handler = logging.FileHandler(f'{os.environ.get("LOG_PATH", "log")}/error.log')
debug_file_handler.setLevel(logging.DEBUG)
info_file_handler.setLevel(logging.INFO)
error_file_handler.setLevel(logging.ERROR)
debug_file_handler.setFormatter(formatter)
info_file_handler.setFormatter(formatter)
error_file_handler.setFormatter(formatter)
logger.addHandler(debug_file_handler)
logger.addHandler(info_file_handler)
logger.addHandler(error_file_handler)
I import this file int my app factory and add the handlers to my app.
# Configure logger
app.logger.removeHandler(default_handler)
app.logger.addHandler(debug_file_handler)
app.logger.addHandler(info_file_handler)
app.logger.addHandler(error_file_handler)
Then in the app factory still, I have a #app.before_request so that logging should occur on every request (for testing purposes).
#app.before_request
def before_request():
elif request.method == 'GET':
app.logger.debug(f'{request.method} {request.base_url}: parameters {dict(request.args)}')
else:
app.logger.debug(f'{request.method} {request.base_url}: parameters {request.json}')
This all works fine on my local machine without a problem. However when I try to deploy to AWS EB, nothing is written to the log files. The log files appear to be created and present when I pull them down via the cli or gui, but nothing is being written to it. I've followed a few tutorials and have added to my .ebextensions file but am still having no luck. Currently my .ebextensions includes a logging.config that looks like this:
files:
"/opt/elasticbeanstalk/tasks/bundlelogs.d/applogs.conf" :
mode: "000777"
owner: root
group: root
content: |
/tmp/*.log
"/opt/elasticbeanstalk/tasks/taillogs.d/applogs.conf" :
mode: "000777"
owner: root
group: root
content: |
/tmp/*.log
The log files appear to be included when I request the logs (both tail and full), but the logs are completely empty even after making multiple request. I've also seen a few answers on here saying I need to include chown wsgi:wsgi in this file, but that has not worked either. I've also tried writing the logs to var/app/current/log and that doesn't work either. The log files are always created, but not written to.
For context, the Elastic Beanstalk platform is Python 3.8 running on Amazon Linux 2.
Any help would be greatly appreciated.
Turns out all I needed to do was set the level of the logger to logging.DEBUG so I could see debug logs.
in __init__.py:
app.logger.setLevel(logging.DEBUG)
And since my app name is src when I configure it, I can grab this logger in other modules with:
import logging
logger = logging.getLogger('src')
I am building a simple flask app, jsonify() works fine on my localhost, it will return the information with new lines and the proper indent in a json format, however when running the exact same code on heroku, it omits the new lines and the indentation
This is how it looks on my localhost and this is on heroku
This is mentioned on the docs for jsonify()
This function's response will be pretty printed if the JSONIFY_PRETTYPRINT_REGULAR config parameter is set to True or the Flask app is running in debug mode
I have both set
app.config['JSONIFY_PRETTYPRINT_REGULAR'] = True
app.run(debug=True)
I tried manually setting the content type to application/json, but that did not help, I even tried using json.dumps() and got the same result
return jsonify(data), 200, {'Content-Type': 'application/json; charset=utf-8'}
Any input on what could be causing heroku not pretty printing?
Edit:
from flask import request, jsonify, Flask
app = Flask(__name__)
#app.route('/test', methods = ['GET'])
def test():
test_dict = {"Key1": "Value1", "Key2": ["Value2","Value2","Value2",]}
print(jsonify(test_dict).headers)
return jsonify(test_dict)
if __name__ == '__main__':
app.run(debug=True)
This simple flask app would pretty print on my localhost like the photos linked above, however on heroku it wont. Looks like it is returning plain text. It can be seen here https://jojoapi.herokuapp.com/test.
I am using gunicorn, not sure if that has any impacts on the output
Edit 2
So, I set manually debug to True as suggested in the comments app.config["DEBUG"] = True and it works properly on heroku now
Some servers (not only Heroku) may not run directly your script and not execute app(debug=True) but they may import app to own code and run it with own arguments app(...own args...) - and this can makes problem.
You can set debug mode in code in different method.
app.config["DEBUG"] = True
Eventually you can try to set environment variable in Linux
export FLASK_DEBUG=1
or
export FLASK_ENV=development
See doc: Debug Mode
Flask doc: Standalone WSGI Containers - it shows servers which import app (as myproject:app) and they may runs with own arguments.
I' trying to combine two independent Flask apps like the example below:
from geventwebsocket import WebSocketServer, Resource
...
server = WebSocketServer(('', 8080), Resource({
'/': frontend,
'/one': flask_app_one,
'/two': flask_app_two}))
server.serve_forever()
Inside each Flask app I declare the full path, isn't that suppose to be relative path, inside flask_app_one:
from flask import Flask
app = Flask(__name__)
#app.route('/one/ping')
def ping():
return 'hello\n'
Why I should specify in #app.route('/one/ping') instead of just #app.route('/ping') since all traffic to /one will be forwarded to the corresponding app?
Let me know if you need any additional info I kept my example clean
Thank you
Finally I have managed to do it with the so called Application Dispatching and the resources found in this page:
http://flask.pocoo.org/docs/0.10/patterns/appdispatch/#app-dispatch
Thanks
I'm trying to set up python and flask on the arduino yun. I've managed to run python files via the /etc/config/uhttpd configuration file:
...
list interpreter ".py=/usr/bin/python"
...
The default path for the website's root is: /www in which I've placed a soft link (apps) to the sd card. So now I can run python programs: http://[ip arduino]/apps/helloworld.py
And when I make my first helloflask.py program and run that via python helloflask.py I can see the result at: http://[ip arduino]:5000
But now I want to configure the uhttpd mini webserver (which is capable to exchange information via CGI) to use the flask setup. The URI: http://flask.pocoo.org/docs/deploying/cgi/#server-setup shows some instructions... but I just don't get it. I've made a directory ../apps/uno in which I've placed a __init__.py file with the following content:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "He Flask!"
In the apps dir I've put a file: cgi.py with this content:
from wsgiref.handlers import CGIHandler
from uno import app
CGIHandler().run(app)
Now I when I browse: http://[ip arduino]/cgi.py get a server error occured, contact the administrator (I think this is the CGI interface from uhttpd).
I just don't grasp the CGI configuration for Flask/uhttpd
I looked into this too and got a little further, I was able to setup a simple hello world but once I tried to do something non-trivial I ran into a big issue that uhttpd doesn't support URL rewriting/aliasing. This means your flask app can only be served at the URL of its .py file instead of at a root like http:// (arduino IP) /flaskapp/. None of the routes inside the app will be visible and makes the whole thing unusable.
However, instead of trying to force flask into uhttpd I had great success running the built in server that flask provides. Take a look at this guide I wrote up that uses flask to serve data from a Yun: https://learn.adafruit.com/smart-measuring-cup/overview
The thing to do is add a call to app.run when the script is run, for example make your flask app look like:
from flask import Flask
app = Flask(__name__)
#app.route("/")
def hello():
return "Hello Flask!"
if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True, threaded=True)
Then log in to the Yun and run the script using python. Flask's built in server should start serving the app on http:// (arduino IP) :5000/. Make sure to include the host='0.0.0.0' as it's required to listen on the Yun's external network interface. You probably also want debug=True so there are better error messages (and live reloading of the server when the code changes), and I found threaded=True helps because the default server only handles one connection at a time. The Yun is a relatively slow processor so don't expect to service a lot of concurrent requests, however it's quite capable for providing a simple REST API or web application for a few users.
If you want this server to always run on bootup, edit the /etc/rc.local file to include a call to python and your script.
I am running nginx + gunicorn + flask
My nginx config looks like:
...
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header Stage "development";
proxy_redirect off;
...
My flask app looks like:
from flask import Flask, request
from werkzeug.contrib.fixers import ProxyFix
app = Flask(__name__)
# configuration settings
if request.headers.get('Stage') == 'production':
app.config.from_object('config.production_config')
else:
app.config.from_object('config.development_config')
#app.route('/')
def index():
return "hello"
app.wsgi_app = ProxyFix(app.wsgi_app)
However,
That doesn't seem to work.
I get a: RuntimeError: working outside of request context
My nginx is setup so that I can have a development/production environment, but I want to be able to say that this "server location" is a development environment, and I want Flask to use the appropriate configuration.
The application config is for the whole application, while request headers are for just one request. The same application generally handles many requests. Therefore you can not set the config based on request headers.
Your code at the module level is executed at server start-up when no request as reached the application yet, so there is no current request. This is what the "working outside of request context" message means.
What you’re trying to do (prod vs. dev config) is better done with an environment variable in the script starting your gunicorn server. If you want both at the same time the easiest is to run two gunicorn servers.
Alternatively, make two application objects, run them both in the same process, and dispatch with a WSGI middleware similar to these: http://flask.pocoo.org/docs/patterns/appdispatch/
This is a bit old but I wanted to add how we accomplish this with flask. The majority of this is adapted from http://flask.pocoo.org/docs/config/ .
In our config.py we define multiple classes (one per environment):
class Config(object):
FOO = 1
BAR = 2
class Development(Config):
BAR = 3
Then in each of our application nodes we set an environment variable in the gunicorn init scripts (for us this lives within a supervisor config but it doesn't have to be).
APPLICATION_ENV='Development'
Then within the flask application during initialization (only runs on server start, not within the request context):
try:
env = os.environ['APPLICATION_ENV']
except KeyError as e:
logging.error('Unknown environment key, defaulting to Development')
env = 'Development'
app.config.from_object('config.%s' % env)
now app.config['BAR'] will be 3.
We also wanted support for local config files (eg on a developer machine or passwords that are deployed from chef directly to the machine and not stored in git). To do that we've expanded on the above to also load a local config based on the app.config['LOCAL_CONFIG'] parameter.
class Development(Config):
BAR = 3
LOCAL_CONFIG = '/etc/localConfig.py'
And then in /etc/localConfig.py
BAR = 4
And again in our app initialization code after the code above loads the initial app.config for the environment:
if 'LOCAL_CONFIG' in app.config:
#try to load the local configuration overrides
if app.config.from_pyfile(app.config['LOCAL_CONFIG'], silent=True):
logging.info('Loaded local config file at %s' % app.config['LOCAL_CONFIG'])
else:
logging.warning('Failed to load local config file at %s - does it exist?' % app.config['LOCAL_CONFIG'])
At this point app.config['BAR'] is 4.
This isn't perfect because if you have dicts in your config you will only be able to override the entire dict, not keys within it. It does accomplish most of what we need though.