Flask destructor - python

I'm building a web application using Flask. I've sub-classed the Flask object so that I can execute a chunk of code before the app exits (the Flask object get's destroyed). When I run this in my terminal and hit ^C, I'm not seeing the "Can you hear me?" message, so I assume that __del__() isn't getting called.
from flask import Flask
class MyFlask (Flask):
def __init__(self, import_name, static_path=None, static_url_path=None,
static_folder='static', template_folder='templates',
instance_path=None, instance_relative_config=False):
Flask.__init__(self, import_name, static_path, static_url_path,
static_folder, template_folder,
instance_path, instance_relative_config)
# Do some stuff ...
def __del__(self):
# Do some stuff ...
print 'Can you hear me?'
app = MyFlask(__name__)
#app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
I'd like this code to execute in the destructor so that it'll run no matter how the application is loaded. i.e, app.run() in testing, gunicorn hello.py in production. Thanks!

Maybe this is possible:
if __name__ == '__main__':
init_db() #or what you need
try:
app.run(host="0.0.0.0")
finally:
# your "destruction" code
print 'Can you hear me?'
I have, however, no clue if you can still use app in the finally block ...

It is not guaranteed that __del__() methods are called for objects that still exist when the interpreter exits. Also, __del__() methods may not have access to global variables, since those may already have been deleted. Relying on destructors in Python is a bad idea.

Related

Gunicorn causes Flask's add_url_rule by causing 404

I'm using Gunicorn on Heroku to try to serve a basic webpage, and if I use the normal route decorator it works fine. For example:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def a():
return "b"
if __name__ == "__main__":
app.run()
This code will run fine, and correctly serve 'b' at the index. However, if instead of using the route decorator I use the add_url_route function, it only responds with a 404.
from flask import Flask
app = Flask(__name__)
def a():
return "b"
if __name__ == "__main__":
app.add_url_rule('/', 'index', a)
app.run()
Here's my Procfile:
web: gunicorn test:app --log-file=-
It's worth noting that when I run this from the command line with Python (python test.py), both work normally. Am I doing something wrong here?
I'm using Python 3.6.3 and Flask 0.12.2.
The app.add_url_rule line is only executed when you directly run the python script. When you just import the script (this is what gunicorn does) then no routes are configured at all and any request will result in a 404.
This also explains why both versions worked for you when executing locally.
If you really want to you could move the app.add_url_rule outside of the main block. I don't see why you would want to do that however. The first example is the way to go.
Note that app.run() is correctly placed inside the main block and should remain there even if you want to use your second example.
A side note: your two routes are not identical. The first one is a route called a at the root path and your second one is a route called index at the root path.

Unit testing Flask app running under uwsgi

I’m relatively new to python and am looking for a pythonic way to handle this practice.
I’ve inherited a fairly trivial Python 2.7 Flask app that runs under uwsgi that I want to add some unit tests to. It does some initialization at indentation level 0 that is required when it’s running in uwsgi but needs to be skipped when under test.
I’m given to understand that often python apps use the
if __name__ == '__main__':
pattern to isolate code that should run when the script is run on its own and should not run when it’s imported. In this case, however, both when the script is run under uwsgi and when the script is imported into the unit tests, __name__ is the same; the name of the script, so I can’t use that to differentiate between uwsgi and unit-testing environments.
This sample code illustrates what I'm working with.
In the Flask application (flask_app.py):
import logging
import bcrypt
from flask import Flask, jsonify, abort, make_response, request
from ConfigParser import SafeConfigParser
# some initialization that only makes sense when running from the uwsgi context on the server
PARSER = SafeConfigParser()
PARSER.read('config.ini')
LOG_FILE = PARSER.get('General', 'logfile')
APP = Flask(__name__)
#APP.route('/', methods=['GET'])
def index
...
#APP.route('/<p1>/<p2>', methods=['PUT'])
def put(p1, p2):
...
if __name__ == '__main__':
APP.run(debug = True, host='0.0.0.0')
In the unit tests (tests.py):
import os
import unittest
from flask import json
from flask_app import APP
class FlaskAppTestCase(unittest.TestCase):
def setUp(self):
self.APP = APP.test_client()
def test_GET(self):
resp = self.APP.get('/')
assert 'Some Html' in resp.data
def test_PUT(self):
resp = self.APP.put('/1/2')
assert 'Got 1, 2' in resp.data
if __name__ == '__main__':
unittest.main()
What I was thinking of doing was to move the initialization so that it only runs when flask_app is being executed by uwsgi and not when it's running via tests.py, perhaps by checking name and determining which path to execute based on that, but when I examine the output of print(name) either when running flask_app under uwsgi or by executing tests.py the output is "flask_app", so I can't seem to use that as a discriminator.
Is there an idiomatic way in python to handle this?
As it turns out the Python module for uWSGI actually offers a mechanism to determine if the app is being run under uWSGI. The uwsgi module is available for import if you are in a uWSGI context, so I ended up checking whether i could import that module and only executing the initialization code if I could.
# detect if we're running under uWSGI; only init if we are, not if we're testing
try:
import uwsgi
IN_UWSGI = True
except ImportError:
IN_UWSGI = False
Then wrap the init code with
if IN_UWSGI:
This seems much more reliable then checking the module name of the module that's doing the import, which was the only other thing I could think of to do.

Global GET Management in Flask

Is there any way of a 'global GET management' in Flask?
For example:
I want to show an error message, via popover, on any page of my flask application. If the user clicks on the 'close' button, the application will make a reload of the page with a new get parameter 'message_read=1'.
I want to catch this GET parameter. I am quite sure there is a better way then writing a check in every single app.route (which are a lot). Could you give me a hint please.
Thank you.
Add a before request function and handle it there. http://flask.pocoo.org/docs/0.12/api/#flask.Flask.before_request
#app.before_request
def do_stuff():
arg = request.args.get('message_read')
You may use decrators . Read about python decorators here
Here is a demonstration of custom decorator with flask.The code below shows a decorator definition and usage for your use case
Code
from flask import Flask,request
from functools import wraps
def popup_message(f):
#wraps(f)
def f_(*args,**argv):
message_read = request.args.get('message_read', None)
if message_read is not None:
return message_read
else:
return f(*args,**argv)
return f_
app = Flask(__name__)
#app.route('/earth')
#popup_message
def hello_earth():
return 'Hello,earth'
#app.route('/world')
#popup_message
def hello_world():
return 'Hello, World!'
app.run()
Usage
Run the app as
python main.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
and try making request to /earth and /world with and without message_read

How to use correctly importlib in a flask controller?

I am trying to load a module according to some settings. I have found a working solution but I need a confirmation from an advanced python developer that this solution is the best performance wise as the API endpoint which will use it will be under heavy load.
The idea is to change the working of an endpoint based on parameters from the user and other systems configuration. I am loading the correct handler class based on these settings. The goal is to be able to easily create new handlers without having to modify the code calling the handlers.
This is a working example :
./run.py :
from flask import Flask, abort
import importlib
import handlers
app = Flask(__name__)
#app.route('/')
def api_endpoint():
try:
endpoint = "simple" # Custom logic to choose the right handler
handlerClass = getattr(importlib.import_module('.'+str(endpoint), 'handlers'), 'Handler')
handler = handlerClass()
except Exception as e:
print(e)
abort(404)
print(handlerClass, handler, handler.value, handler.name())
# Handler processing. Not yet implemented
return "Hello World"
if __name__ == "__main__":
app.run(host='0.0.0.0', port=8080, debug=True)
One "simple" handler example. A handler is a module which needs to define an Handler class :
./handlers/simple.py :
import os
class Handler:
def __init__(self):
self.value = os.urandom(5)
def name(self):
return "simple"
If I understand correctly, the import is done on each query to the endpoint. It means IO in the filesystem with lookup for the modules, ...
Is it the correct/"pythonic" way to implement this strategy ?
Question moved to codereview. Thanks all for your help : https://codereview.stackexchange.com/questions/96533/extension-pattern-in-a-flask-controller-using-importlib
I am closing this thread.

Flask-Script How to get to the current app?

I'm trying to get access to the current app instance from a Flask-Script manager.command.
This errors out (url_map is a property of flask.app)
#manager.command
def my_function():
x = app.url_map # this fails, because app is a callable
print "hi"
This works, but I don't like having to add parens next to app.
#manager.command
def my_function():
x = app().url_map
print "hi"
The debugger shows that app is a callable. That has to do with the way that I'm creating the app instance. I'm following this pattern:
def create_app(settings=None, app_name=None, blueprints=None):
...lots of stuff...
app = flask.Flask(app_name)
...lots of stuff...
return app
def create_manager(app):
manager = Manager(app)
#manager.command
def my_function():
x = app.url_map
print "hi"
def main():
manager = create_manager(create_app)
manager.run()
if __name__ == "__main__":
main()
The docs from flask-script say about the app parameters on Manager(app):
app – Flask instance, or callable returning a Flask instance.
I'm comfortable with putting a callable in there because the docs say it's OK. :-) Plus I've seen others do it like that.
But now I have this peripheral command that I'd like to add and it's forcing me to use the app with parens and that smells wrong. What am I doing wrong?
EDIT: I did some experiments. This is definitely wrong. By adding the parens, the app instance is getting recreated a second time.
Use flask.current_app
This works:
import flask
... other stuff ...
#manager.command
def my_function():
x = flask.current_app.url_map
print "hi"
I 'overthunk' it. :-)

Categories