how to run the code before the app.run() in flask? - python

I'm new on flask.I configured a server with flask+gunicorn.
the code file called test.py like this:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def test():
return aa+"world!"
if __name__ == '__main__':
aa = "hello"
app.run()
run it using:gunicorn -b 0.0.0.0:8080 test:app
I got a mistake:NameError: name 'aa' is not defined.
I want some codes like variable aa runing before gunicorn.
How to do that?

Put in a small block just before your #app.route and you dont need the last block in the question
#app.before_first_request
def _declareStuff():
global aa
aa='hello'

Just declare aa outside of "__main__", in the global scope of the file.
from flask import Flask
app = Flask(__name__)
#app.route('/')
def test():
return aa+"world!"
aa = "hello"
if __name__ == '__main__':
app.run()
The code in the if __name__ == '__main__': block executes only if the Python code is run as a script, e.g., from the command line. Gunicorn imports the file, so in that case the code in __main__ will not be executed.
Note that if it is your intention to modify the value of aa then different requests can produce different results depending on how many requests each gunicorn worker process has handled. e.g.:
from flask import Flask
app = Flask(__name__)
#app.route('/')
def test():
global counter
counter += 1
return "{} world! {}".format('aa', counter)
counter = 0
if __name__ == '__main__':
app.run()
Run the above script with more than one worker (gunicorn -w 2 ...) and make several requests to the URL. You should see that the counter is not always contiguous.

As of Flask 2.2, the #app.before_first_request decorator suggested by Vipluv in their answer is deprecated and will be removed in 2.3.
Deprecated since version 2.2: Will be removed in Flask 2.3. Run setup code when creating the application instead.
The equivalent can be done by manually pushing the app context, as suggested by Enkum :
# In place of something like this
#app.before_first_request
def create_tables():
db.create_all()
...
# USE THIS INSTEAD
with app.app_context():
db.create_all()

Related

python flask before_first_request_funcs

In my python3 flask application I would like to execute a couple of recurring tasks before the first request.
In order to achieve this, I want to make use of the#app.before_first_request_funcs.
Can anyone please give me an example usage of #app.before_first_request_funcs?
Here is my sample code:
import threading
import time
from flask import Flask
app = Flask(__name__)
def activate_job():
def run_job():
while True:
print("recurring task")
time.sleep(3)
thread = threading.Thread(target=run_job())
thread.start()
def activate_job2():
def run_job2():
while True:
print("recurring task2")
time.sleep(3)
thread = threading.Thread(target=run_job2())
thread.start()
#app.after_first_request(activate_job())
#app.before_first_request(activate_job2())
#app.route('/')
def home():
return {"action": "This has done something"}
if __name__ == '__main__':
print(app.before_first_request_funcs)
app.run()
As per the documentation, you should use #app.before_first_request to do what you want.
from flask import Flask
app = Flask(__name__)
def some_func(some_arg):
print('coucou')
# #app.before_first_request(some_func)
#app.route('/')
def home():
return {"action" : "This has done something"}
if __name__ == '__main__':
print(app.before_first_request_funcs)
app.run()
You can see the behavior of the method before_first_request_funcs that is not a decorator by commenting and uncommenting the decorator before_first_request.
If it is commented, it'll print an empty list, and if you uncomment the line, it'll return a list of one element containing the function some_func object (for me, it was [<function some_func at 0x0000021393A0AD90>]).

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.

How to pass an arbitrary argument to Flask through app.run()?

I would like to pass an object to a newly initiated flask app. I tried following the solution from the question: how-can-i-make-command-line-arguments-visible-to-flask-routes
Edit
I would like to take a value that I pick up from initiating the python script from the command line.
ie.
$ run python flaskTest.py -a goo
I am not seeing the difference between this and the solution to the question I am trying to replicate.
Edit
Thus, I tried the following:
from flask import Flask
app = Flask(__name__)
print('Passed item: ', app.config.get('foo'))
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-a')
args = parser.parse_args()
val = args.a
app.config['foo'] = val
app.run()
Hoping to get the result...
'Passed item: Goo'
Is there a method for passing an arbitrary object through the initialization with app.run()?
Well the script is executing from top to bottom, so you can't print something you don't have yet. Putting the print statement inside a classic flask factory function allow you to first parse command line, then get your object and then use it:
from flask import Flask
def create_app(foo):
app = Flask(__name__)
app.config['foo'] = foo
print('Passed item: ', app.config['foo'])
return app
if __name__ == '__main__':
from argparse import ArgumentParser
parser = ArgumentParser()
parser.add_argument('-a')
args = parser.parse_args()
foo = args.a
app = create_app(foo)
app.run()
So, the problem is that you're trying to access the value before you define it. You would need to do something like this in your case:
from flask import Flask
app = Flask(__name__)
app.config['foo'] = 'Goo'
print('Passed item: ', app.config['foo'])
if __name__ == '__main__':
app.run()
If you're trying to access that value while loading some third module, you'll need to define the value somewhere ahead of time.
An update for more recent versions of Flask: when running through flask run you can now invoke an app factory and even pass arguments (docs).
Example code:
from flask import Flask
def create_app(foo=None):
app = Flask(__name__)
app.config["foo"] = foo
return app
# if __name__ == "__main__": ... not necessary
Assuming it is saved as flaskTest.py, you can run it using:
export FLASK_APP="flaskTest:create_app('value of foo')"
flask run

How can I make command line arguments visible to Flask routes?

I am using Flask to build a tool to view data locally in a browser. I want to pass the directory containing the data as a command line argument, and then pass it to the appropriate routing function to do the rendering.
This does what I want, but with global variables:
dataDir = None
def initializeData(pathname):
global dataDir
dataDir = pathname
#app.route('/')
def home():
# Use dataDir as desired
if __name__ == '__main__':
initializeData(sys.argv[1])
app = Flask(__name__)
app.run()
Is there a better way to communicate between the command line and my routes?
Your flask app has a config property. Also, this code will fail with a NameError. You want something like this:
import sys
from flask import Flask
app = Flask(__name__)
#app.route('/')
def home():
return 'You wanted {!r} directory'.format(app.config.get('some_setting'))
if __name__ == '__main__':
app.config['some_setting'] = sys.argv[1]
app.run()
Consider using app.config.from_json('config.json') so that you can configure your env parameters in a json file.

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