Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added the ability to trigger functions before the first request to th…

…e application
  • Loading branch information...
commit 02a131746074203dc8d747308f4d2c7ece8aa743 1 parent 5500986
@mitsuhiko authored
View
2  CHANGES
@@ -21,6 +21,8 @@ Relase date to be decided, codename to be chosen.
- Flask in debug mode will now complain with an assertion error if a view
was attached after the first request was handled. This gives earlier
feedback when users forget to import view code ahead of time.
+- Added the ability to register callbacks that are only triggered once at
+ the beginning of the first request. (:meth:`Flask.before_first_request`)
Version 0.7.3
-------------
View
52 flask/app.py
@@ -291,6 +291,13 @@ def __init__(self, import_name, static_path=None, static_url_path=None,
#: function here, use the :meth:`before_request` decorator.
self.before_request_funcs = {}
+ #: A lists of functions that should be called at the beginning of the
+ #: first request to this instance. To register a function here, use
+ #: the :meth:`before_first_request` decorator.
+ #:
+ #: .. versionadded:: 0.8
+ self.before_first_request_funcs = []
+
#: A dictionary with lists of functions that should be called after
#: each request. The key of the dictionary is the name of the blueprint
#: this function is active for, `None` for all requests. This can for
@@ -386,6 +393,7 @@ def __init__(self, import_name, static_path=None, static_url_path=None,
# tracks internally if the application already handled at least one
# request.
self._got_first_request = False
+ self._before_request_lock = Lock()
# register the static folder for the application. Do that even
# if the folder does not exist. First of all it might be created
@@ -474,6 +482,15 @@ def jinja_env(self):
return rv
+ @property
+ def got_first_request(self):
+ """This attribute is set to `True` if the application started
+ handling the first request.
+
+ .. versionadded:: 0.8
+ """
+ return self._got_first_request
+
def create_jinja_environment(self):
"""Creates the Jinja2 environment based on :attr:`jinja_options`
and :meth:`select_jinja_autoescape`. Since 0.7 this also adds
@@ -581,7 +598,13 @@ def run(self, host='127.0.0.1', port=5000, **options):
self.debug = options.pop('debug')
options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', self.debug)
- return run_simple(host, port, self, **options)
+ try:
+ run_simple(host, port, self, **options)
+ finally:
+ # reset the first request information if the development server
+ # resetted normally. This makes it possible to restart the server
+ # without reloader and that stuff from an interactive shell.
+ self._got_first_request = False
def test_client(self, use_cookies=True):
"""Creates a test client for this application. For information
@@ -941,6 +964,15 @@ def before_request(self, f):
return f
@setupmethod
+ def before_first_request(self, f):
+ """Registers a function to be run before the first request to this
+ instance of the application.
+
+ .. versionadded:: 0.8
+ """
+ self.before_first_request_funcs.append(f)
+
+ @setupmethod
def after_request(self, f):
"""Register a function to be run after each request. Your function
must take one parameter, a :attr:`response_class` object and return
@@ -1131,7 +1163,7 @@ def full_dispatch_request(self):
.. versionadded:: 0.7
"""
- self._got_first_request = True
+ self.try_trigger_before_first_request_functions()
try:
request_started.send(self)
rv = self.preprocess_request()
@@ -1144,6 +1176,22 @@ def full_dispatch_request(self):
request_finished.send(self, response=response)
return response
+ def try_trigger_before_first_request_functions(self):
+ """Called before each request and will ensure that it triggers
+ the :attr:`before_first_request_funcs` and only exactly once per
+ application instance (which means process usually).
+
+ .. versionadded:: 0.8
+ """
+ if self._got_first_request:
+ return
+ with self._before_request_lock:
+ if self._got_first_request:
+ return
+ self._got_first_request = True
+ for func in self.before_first_request_funcs:
+ func()
+
def make_default_options_response(self):
"""This method is called to create the default `OPTIONS` response.
This can be changed through subclassing to change the default
View
7 flask/blueprints.py
@@ -199,6 +199,13 @@ def before_app_request(self, f):
.setdefault(None, []).append(f))
return f
+ def before_app_first_request(self, f):
+ """Like :meth:`Flask.before_first_request`. Such a function is
+ executed before the first request to the application.
+ """
+ self.record_once(lambda s: s.app.before_first_request_funcs.append(f))
+ return f
+
def after_request(self, f):
"""Like :meth:`Flask.after_request` but for a blueprint. This function
is only executed after each request that is handled by a function of
View
15 tests/flask_tests.py
@@ -950,6 +950,7 @@ def test_debug_mode_complains_after_first_request(self):
@app.route('/')
def index():
return 'Awesome'
+ self.assert_(not app.got_first_request)
self.assertEqual(app.test_client().get('/').data, 'Awesome')
try:
@app.route('/foo')
@@ -965,6 +966,20 @@ def broken():
def working():
return 'Meh'
self.assertEqual(app.test_client().get('/foo').data, 'Meh')
+ self.assert_(app.got_first_request)
+
+ def test_before_first_request_functions(self):
+ got = []
+ app = flask.Flask(__name__)
+ @app.before_first_request
+ def foo():
+ got.append(42)
+ c = app.test_client()
+ c.get('/')
+ self.assertEqual(got, [42])
+ c.get('/')
+ self.assertEqual(got, [42])
+ self.assert_(app.got_first_request)
class JSONTestCase(unittest.TestCase):
Please sign in to comment.
Something went wrong with that request. Please try again.