Browse Source

Added the ability to trigger functions before the first request to the application

pull/300/head
Armin Ronacher 13 years ago
parent
commit
02a1317460
  1. 2
      CHANGES
  2. 52
      flask/app.py
  3. 7
      flask/blueprints.py
  4. 15
      tests/flask_tests.py

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 - 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 was attached after the first request was handled. This gives earlier
feedback when users forget to import view code ahead of time. 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 Version 0.7.3
------------- -------------

52
flask/app.py

@ -291,6 +291,13 @@ class Flask(_PackageBoundObject):
#: function here, use the :meth:`before_request` decorator. #: function here, use the :meth:`before_request` decorator.
self.before_request_funcs = {} 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 #: A dictionary with lists of functions that should be called after
#: each request. The key of the dictionary is the name of the blueprint #: 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 #: this function is active for, `None` for all requests. This can for
@ -386,6 +393,7 @@ class Flask(_PackageBoundObject):
# tracks internally if the application already handled at least one # tracks internally if the application already handled at least one
# request. # request.
self._got_first_request = False self._got_first_request = False
self._before_request_lock = Lock()
# register the static folder for the application. Do that even # register the static folder for the application. Do that even
# if the folder does not exist. First of all it might be created # if the folder does not exist. First of all it might be created
@ -474,6 +482,15 @@ class Flask(_PackageBoundObject):
return rv 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): def create_jinja_environment(self):
"""Creates the Jinja2 environment based on :attr:`jinja_options` """Creates the Jinja2 environment based on :attr:`jinja_options`
and :meth:`select_jinja_autoescape`. Since 0.7 this also adds and :meth:`select_jinja_autoescape`. Since 0.7 this also adds
@ -581,7 +598,13 @@ class Flask(_PackageBoundObject):
self.debug = options.pop('debug') self.debug = options.pop('debug')
options.setdefault('use_reloader', self.debug) options.setdefault('use_reloader', self.debug)
options.setdefault('use_debugger', 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): def test_client(self, use_cookies=True):
"""Creates a test client for this application. For information """Creates a test client for this application. For information
@ -940,6 +963,15 @@ class Flask(_PackageBoundObject):
self.before_request_funcs.setdefault(None, []).append(f) self.before_request_funcs.setdefault(None, []).append(f)
return 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 @setupmethod
def after_request(self, f): def after_request(self, f):
"""Register a function to be run after each request. Your function """Register a function to be run after each request. Your function
@ -1131,7 +1163,7 @@ class Flask(_PackageBoundObject):
.. versionadded:: 0.7 .. versionadded:: 0.7
""" """
self._got_first_request = True self.try_trigger_before_first_request_functions()
try: try:
request_started.send(self) request_started.send(self)
rv = self.preprocess_request() rv = self.preprocess_request()
@ -1144,6 +1176,22 @@ class Flask(_PackageBoundObject):
request_finished.send(self, response=response) request_finished.send(self, response=response)
return 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): def make_default_options_response(self):
"""This method is called to create the default `OPTIONS` response. """This method is called to create the default `OPTIONS` response.
This can be changed through subclassing to change the default This can be changed through subclassing to change the default

7
flask/blueprints.py

@ -199,6 +199,13 @@ class Blueprint(_PackageBoundObject):
.setdefault(None, []).append(f)) .setdefault(None, []).append(f))
return 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): def after_request(self, f):
"""Like :meth:`Flask.after_request` but for a blueprint. This function """Like :meth:`Flask.after_request` but for a blueprint. This function
is only executed after each request that is handled by a function of is only executed after each request that is handled by a function of

15
tests/flask_tests.py

@ -950,6 +950,7 @@ class BasicFunctionalityTestCase(unittest.TestCase):
@app.route('/') @app.route('/')
def index(): def index():
return 'Awesome' return 'Awesome'
self.assert_(not app.got_first_request)
self.assertEqual(app.test_client().get('/').data, 'Awesome') self.assertEqual(app.test_client().get('/').data, 'Awesome')
try: try:
@app.route('/foo') @app.route('/foo')
@ -965,6 +966,20 @@ class BasicFunctionalityTestCase(unittest.TestCase):
def working(): def working():
return 'Meh' return 'Meh'
self.assertEqual(app.test_client().get('/foo').data, '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): class JSONTestCase(unittest.TestCase):

Loading…
Cancel
Save