Permalink
Browse files

Merge branch 'master' into fix-issue-220

  • Loading branch information...
2 parents cf38eec + e78e2a1 commit 3703156d2ad779d6ff947800cb343e9822422a53 @jfinkels committed May 2, 2012
Showing with 244 additions and 160 deletions.
  1. +11 −8 CHANGES
  2. +33 −32 docs/advanced_foreword.rst
  3. +56 −44 docs/becomingbig.rst
  4. +7 −4 docs/config.rst
  5. +1 −0 docs/contents.rst.inc
  6. +39 −37 docs/foreword.rst
  7. +13 −7 flask/app.py
  8. +2 −1 flask/ctx.py
  9. +32 −19 flask/helpers.py
  10. +10 −0 flask/testsuite/appctx.py
  11. +23 −0 flask/testsuite/blueprints.py
  12. +17 −8 flask/testsuite/helpers.py
View
19 CHANGES
@@ -55,18 +55,21 @@ Relase date to be decided, codename to be chosen.
- View functions can now return a tuple with the first instance being an
instance of :class:`flask.Response`. This allows for returning
``jsonify(error="error msg"), 400`` from a view function.
-- :class:`flask.Flask` now provides a `get_send_file_options` hook for
- subclasses to override behavior of serving static files from Flask when using
- :meth:`flask.Flask.send_static_file` based on keywords in
- :func:`flask.helpers.send_file`. This hook is provided a filename, which for
- example allows changing cache controls by file extension. The default
- max-age for `send_static_file` can be configured through a new
- ``SEND_FILE_MAX_AGE_DEFAULT`` configuration variable, regardless of whether
- the `get_send_file_options` hook is used.
+- :class:`~flask.Flask` and :class:`~flask.Blueprint` now provide a
+ :meth:`~flask.Flask.get_send_file_max_age` hook for subclasses to override
+ behavior of serving static files from Flask when using
+ :meth:`flask.Flask.send_static_file` (used for the default static file
+ handler) and :func:`~flask.helpers.send_file`. This hook is provided a
+ filename, which for example allows changing cache controls by file extension.
+ The default max-age for `send_file` and static files can be configured
+ through a new ``SEND_FILE_MAX_AGE_DEFAULT`` configuration variable, which is
+ used in the default `get_send_file_max_age` implementation.
- Fixed an assumption in sessions implementation which could break message
flashing on sessions implementations which use external storage.
- Changed the behavior of tuple return values from functions. They are no
longer arguments to the response object, they now have a defined meaning.
+- Added :attr:`flask.Flask.request_globals_class` to allow a specific class to
+ be used on creation of the :data:`~flask.g` instance of each request.
Version 0.8.1
-------------
View
65 docs/advanced_foreword.rst
@@ -1,27 +1,26 @@
+.. _advanced_foreword:
+
Foreword for Experienced Programmers
====================================
-This chapter is for programmers who have worked with other frameworks in the
-past, and who may have more specific or esoteric concerns that the typical
-user.
-
-Threads in Flask
-----------------
+Thread-Locals in Flask
+----------------------
-One of the design decisions with Flask was that simple tasks should be simple;
+One of the design decisions in Flask was that simple tasks should be simple;
they should not take a lot of code and yet they should not limit you. Because
-of that we made a few design choices that some people might find surprising or
-unorthodox. For example, Flask uses thread-local objects internally so that
-you don’t have to pass objects around from function to function within a
-request in order to stay threadsafe. While this is a really easy approach and
-saves you a lot of time, it might also cause some troubles for very large
-applications because changes on these thread-local objects can happen anywhere
-in the same thread. In order to solve these problems we don’t hide the thread
-locals for you but instead embrace them and provide you with a lot of tools to
-make it as pleasant as possible to work with them.
+of that, Flask has few design choices that some people might find surprising or
+unorthodox. For example, Flask uses thread-local objects internally so that you
+don’t have to pass objects around from function to function within a request in
+order to stay threadsafe. This approach is convenient, but requires a valid
+request context for dependency injection or when attempting to reuse code which
+uses a value pegged to the request. The Flask project is honest about
+thread-locals, does not hide them, and calls out in the code and documentation
+where they are used.
-Web Development is Dangerous
-----------------------------
+Develop for the Web with Caution
+--------------------------------
+
+Always keep security in mind when building web applications.
If you write a web application, you are probably allowing users to register
and leave their data on your server. The users are entrusting you with data.
@@ -30,22 +29,22 @@ you still want that data to be stored securely.
Unfortunately, there are many ways the security of a web application can be
compromised. Flask protects you against one of the most common security
-problems of modern web applications: cross-site scripting (XSS). Unless
-you deliberately mark insecure HTML as secure, Flask and the underlying
-Jinja2 template engine have you covered. But there are many more ways to
-cause security problems.
+problems of modern web applications: cross-site scripting (XSS). Unless you
+deliberately mark insecure HTML as secure, Flask and the underlying Jinja2
+template engine have you covered. But there are many more ways to cause
+security problems.
-The documentation will warn you about aspects of web development that
-require attention to security. Some of these security concerns
-are far more complex than one might think, and we all sometimes underestimate
-the likelihood that a vulnerability will be exploited - until a clever
-attacker figures out a way to exploit our applications. And don't think
-that your application is not important enough to attract an attacker.
-Depending on the kind of attack, chances are that automated bots are
-probing for ways to fill your database with spam, links to malicious
-software, and the like.
+The documentation will warn you about aspects of web development that require
+attention to security. Some of these security concerns are far more complex
+than one might think, and we all sometimes underestimate the likelihood that a
+vulnerability will be exploited - until a clever attacker figures out a way to
+exploit our applications. And don't think that your application is not
+important enough to attract an attacker. Depending on the kind of attack,
+chances are that automated bots are probing for ways to fill your database with
+spam, links to malicious software, and the like.
-So always keep security in mind when doing web development.
+Flask is no different from any other framework in that you the developer must
+build with caution, watching for exploits when building to your requirements.
The Status of Python 3
----------------------
@@ -65,3 +64,5 @@ using Python 2.6 and 2.7 with activated Python 3 warnings during
development. If you plan on upgrading to Python 3 in the near future we
strongly recommend that you read `How to write forwards compatible
Python code <http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/>`_.
+
+Continue to :ref:`installation` or the :ref:`quickstart`.
View
100 docs/becomingbig.rst
@@ -3,45 +3,57 @@
Becoming Big
============
-Your application is becoming more and more complex? If you suddenly
-realize that Flask does things in a way that does not work out for your
-application there are ways to deal with that.
-
-Flask is powered by Werkzeug and Jinja2, two libraries that are in use at
-a number of large websites out there and all Flask does is bring those
-two together. Being a microframework Flask does not do much more than
-combining existing libraries - there is not a lot of code involved.
-What that means for large applications is that it's very easy to take the
-code from Flask and put it into a new module within the applications and
-expand on that.
-
-Flask is designed to be extended and modified in a couple of different
-ways:
-
-- Flask extensions. For a lot of reusable functionality you can create
- extensions. For extensions a number of hooks exist throughout Flask
- with signals and callback functions.
-
-- Subclassing. The majority of functionality can be changed by creating
- a new subclass of the :class:`~flask.Flask` class and overriding
- methods provided for this exact purpose.
-
-- Forking. If nothing else works out you can just take the Flask
- codebase at a given point and copy/paste it into your application
- and change it. Flask is designed with that in mind and makes this
- incredible easy. You just have to take the package and copy it
- into your application's code and rename it (for example to
- `framework`). Then you can start modifying the code in there.
-
-Why consider Forking?
+Here are your options when growing your codebase or scaling your application.
+
+Read the Source.
+----------------
+
+Flask started in part to demonstrate how to build your own framework on top of
+existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it
+developed, it became useful to a wide audience. As you grow your codebase,
+don't just use Flask -- understand it. Read the source. Flask's code is
+written to be read; it's documentation published so you can use its internal
+APIs. Flask sticks to documented APIs in upstream libraries, and documents its
+internal utilities so that you can find the hook points needed for your
+project.
+
+Hook. Extend.
+-------------
+
+The :ref:`api` docs are full of available overrides, hook points, and
+:ref:`signals`. You can provide custom classes for things like the request and
+response objects. Dig deeper on the APIs you use, and look for the
+customizations which are available out of the box in a Flask release. Look for
+ways in which your project can be refactored into a collection of utilities and
+Flask extensions. Explore the many extensions in the community, and look for
+patterns to build your own extensions if you do not find the tools you need.
+
+Subclass.
+---------
+
+The :class:`~flask.Flask` class has many methods designed for subclassing. You
+can quickly add or customize behavior by subclassing :class:`~flask.Flask` (see
+the linked method docs) and using that subclass wherever you instantiate an
+application class. This works well with :ref:`app-factories`.
+
+Wrap with middleware.
---------------------
-The majority of code of Flask is within Werkzeug and Jinja2. These
-libraries do the majority of the work. Flask is just the paste that glues
-those together. For every project there is the point where the underlying
-framework gets in the way (due to assumptions the original developers
-had). This is natural because if this would not be the case, the
-framework would be a very complex system to begin with which causes a
+The :ref:`app-dispatch` chapter shows in detail how to apply middleware. You
+can introduce WSGI middleware to wrap your Flask instances and introduce fixes
+and changes at the layer between your Flask application and your HTTP
+server. Werkzeug includes several `middlewares
+<http://werkzeug.pocoo.org/docs/middlewares/>`_.
+
+Fork.
+-----
+
+If none of the above options work, fork Flask. The majority of code of Flask
+is within Werkzeug and Jinja2. These libraries do the majority of the work.
+Flask is just the paste that glues those together. For every project there is
+the point where the underlying framework gets in the way (due to assumptions
+the original developers had). This is natural because if this would not be the
+case, the framework would be a very complex system to begin with which causes a
steep learning curve and a lot of user frustration.
This is not unique to Flask. Many people use patched and modified
@@ -55,8 +67,8 @@ Furthermore integrating upstream changes can be a complex process,
depending on the number of changes. Because of that, forking should be
the very last resort.
-Scaling like a Pro
-------------------
+Scale like a pro.
+-----------------
For many web applications the complexity of the code is less an issue than
the scaling for the number of users or data entries expected. Flask by
@@ -78,11 +90,11 @@ majority of servers are using either threads, greenlets or separate
processes to achieve concurrency which are all methods well supported by
the underlying Werkzeug library.
-Dialogue with the Community
+Discuss with the community.
---------------------------
-The Flask developers are very interested to keep everybody happy, so as
-soon as you find an obstacle in your way, caused by Flask, don't hesitate
-to contact the developers on the mailinglist or IRC channel. The best way
-for the Flask and Flask-extension developers to improve it for larger
+The Flask developers keep the framework accessible to users with codebases big
+and small. If you find an obstacle in your way, caused by Flask, don't hesitate
+to contact the developers on the mailinglist or IRC channel. The best way for
+the Flask and Flask extension developers to improve the tools for larger
applications is getting feedback from users.
View
11 docs/config.rst
@@ -111,12 +111,15 @@ The following configuration values are used internally by Flask:
content length greater than this by
returning a 413 status code.
``SEND_FILE_MAX_AGE_DEFAULT``: Default cache control max age to use with
- :meth:`flask.Flask.send_static_file`, in
+ :meth:`~flask.Flask.send_static_file` (the
+ default static file handler) and
+ :func:`~flask.send_file`, in
seconds. Override this value on a per-file
basis using the
- :meth:`flask.Flask.get_send_file_options` and
- :meth:`flask.Blueprint.get_send_file_options`
- hooks. Defaults to 43200 (12 hours).
+ :meth:`~flask.Flask.get_send_file_max_age`
+ hook on :class:`~flask.Flask` or
+ :class:`~flask.Blueprint`,
+ respectively. Defaults to 43200 (12 hours).
``TRAP_HTTP_EXCEPTIONS`` If this is set to ``True`` Flask will
not execute the error handlers of HTTP
exceptions but instead treat the
View
1 docs/contents.rst.inc
@@ -9,6 +9,7 @@ instructions for web development with Flask.
:maxdepth: 2
foreword
+ advanced_foreword
installation
quickstart
tutorial/index
View
76 docs/foreword.rst
@@ -8,48 +8,50 @@ should or should not be using it.
What does "micro" mean?
-----------------------
-“Micro” does not mean that your whole web application has to fit into
-a single Python file (although it certainly can). Nor does it mean
-that Flask is lacking in functionality. The "micro" in microframework
-means Flask aims to keep the core simple but extensible. Flask won't make
-many decisions for you, such as what database to use. Those decisions that
-it does make, such as what templating engine to use, are easy to change.
-Everything else is up to you, so that Flask can be everything you need
-and nothing you don't.
+“Micro” does not mean that your whole web application has to fit into a single
+Python file, although it certainly can. Nor does it mean that Flask is lacking
+in functionality. The "micro" in microframework means Flask aims to keep the
+core simple but extensible. Flask won't make many decisions for you, such as
+what database to use. Those decisions that it does make, such as what
+templating engine to use, are easy to change. Everything else is up to you, so
+that Flask can be everything you need and nothing you don't.
By default, Flask does not include a database abstraction layer, form
validation or anything else where different libraries already exist that can
-handle that. Instead, FLask extensions add such functionality to your
-application as if it was implemented in Flask itself. Numerous extensions
+handle that. Instead, Flask supports extensions to add such functionality to
+your application as if it was implemented in Flask itself. Numerous extensions
provide database integration, form validation, upload handling, various open
-authentication technologies, and more. Flask may be "micro", but the
-possibilities are endless.
+authentication technologies, and more. Flask may be "micro", but it's ready for
+production use on a variety of needs.
-Convention over Configuration
+Configuration and Conventions
-----------------------------
-Flask is based on convention over configuration, which means that many things
-are preconfigured. For example, by convention templates and static files are
-stored in subdirectories within the application's Python source tree. While
-this can be changed you usually don't have to. We want to minimize the time
-you need to spend in order to get up and running, without assuming things
-about your needs.
-
-Growing Up
-----------
-
-Since Flask is based on a very solid foundation there is not a lot of code in
-Flask itself. As such it's easy to adapt even for large applications and we
-are making sure that you can either configure it as much as possible by
-subclassing things or by forking the entire codebase. If you are interested
-in that, check out the :ref:`becomingbig` chapter.
-
-If you are curious about the Flask design principles, head over to the section
-about :ref:`design`.
-
-For the Stalwart and Wizened...
--------------------------------
-
-If you're more curious about the minutiae of Flask's implementation, and
-whether its structure is right for your needs, read the
+Flask has many configuration values, with sensible defaults, and a few
+conventions when getting started. By convention templates and static files are
+stored in subdirectories within the application's Python source tree, with the
+names `templates` and `static` respectively. While this can be changed you
+usually don't have to, especially when getting started.
+
+Growing with Flask
+------------------
+
+Once you have Flask up and running, you'll find a variety of extensions
+available in the community to integrate your project for production. The Flask
+core team reviews extensions and ensures approved extensions do not break with
+future releases.
+
+As your codebase grows, you are free to make the design decisions appropriate
+for your project. Flask will continue to provide a very simple glue layer to
+the best that Python has to offer. You can implement advanced patterns in
+SQLAlchemy or another database tool, introduce non-relational data persistence
+as appropriate, and take advantage of framework-agnostic tools built for WSGI,
+the Python web interface.
+
+Flask includes many hooks to customize its behavior. Should you need more
+customization, the Flask class is built for subclassing. If you are interested
+in that, check out the :ref:`becomingbig` chapter. If you are curious about
+the Flask design principles, head over to the section about :ref:`design`.
+
+Continue to :ref:`installation`, the :ref:`quickstart`, or the
:ref:`advanced_foreword`.
View
20 flask/app.py
@@ -28,7 +28,7 @@
find_package, JSONEncoder
from .wrappers import Request, Response
from .config import ConfigAttribute, Config
-from .ctx import RequestContext, AppContext
+from .ctx import RequestContext, AppContext, _RequestGlobals
from .globals import _request_ctx_stack, request
from .sessions import SecureCookieSessionInterface
from .module import blueprint_is_module
@@ -148,6 +148,18 @@ class Flask(_PackageBoundObject):
#: :class:`~flask.Response` for more information.
response_class = Response
+ #: The class that is used for the :data:`~flask.g` instance.
+ #:
+ #: Example use cases for a custom class:
+ #:
+ #: 1. Store arbitrary attributes on flask.g.
+ #: 2. Add a property for lazy per-request database connectors.
+ #: 3. Return None instead of AttributeError on expected attributes.
+ #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g.
+ #:
+ #: .. versionadded:: 0.9
+ request_globals_class = _RequestGlobals
+
#: The debug flag. Set this to `True` to enable debugging of the
#: application. In debug mode the debugger will kick in when an unhandled
#: exception ocurrs and the integrated server will automatically reload
@@ -1073,12 +1085,6 @@ def _register_error_handler(self, key, code_or_exception, f):
self.error_handler_spec.setdefault(key, {}).setdefault(None, []) \
.append((code_or_exception, f))
- def get_send_file_options(self, filename):
- # Override: Hooks in SEND_FILE_MAX_AGE_DEFAULT config.
- options = super(Flask, self).get_send_file_options(filename)
- options['cache_timeout'] = self.config['SEND_FILE_MAX_AGE_DEFAULT']
- return options
-
@setupmethod
def template_filter(self, name=None):
"""A decorator that is used to register custom template filter.
View
3 flask/ctx.py
@@ -18,6 +18,7 @@
class _RequestGlobals(object):
+ """A plain object."""
pass
@@ -139,7 +140,7 @@ def __init__(self, app, environ):
self.app = app
self.request = app.request_class(environ)
self.url_adapter = app.create_url_adapter(self.request)
- self.g = _RequestGlobals()
+ self.g = app.request_globals_class()
self.flashes = None
self.session = None
View
51 flask/helpers.py
@@ -409,7 +409,7 @@ def get_flashed_messages(with_categories=False, category_filter=[]):
def send_file(filename_or_fp, mimetype=None, as_attachment=False,
attachment_filename=None, add_etags=True,
- cache_timeout=60 * 60 * 12, conditional=False):
+ cache_timeout=None, conditional=False):
"""Sends the contents of a file to the client. This will use the
most efficient method available and configured. By default it will
try to use the WSGI server's file_wrapper support. Alternatively
@@ -423,10 +423,6 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
guessing requires a `filename` or an `attachment_filename` to be
provided.
- Note `get_send_file_options` in :class:`flask.Flask` hooks the
- ``SEND_FILE_MAX_AGE_DEFAULT`` configuration variable to set the default
- cache_timeout.
-
Please never pass filenames to this function from user sources without
checking them first. Something like this is usually sufficient to
avoid security problems::
@@ -446,6 +442,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
able to, otherwise attach an etag yourself. This functionality
will be removed in Flask 1.0
+ .. versionchanged:: 0.9
+ cache_timeout pulls its default from application config, when None.
+
:param filename_or_fp: the filename of the file to send. This is
relative to the :attr:`~Flask.root_path` if a
relative path is specified.
@@ -462,7 +461,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
differs from the file's filename.
:param add_etags: set to `False` to disable attaching of etags.
:param conditional: set to `True` to enable conditional responses.
- :param cache_timeout: the timeout in seconds for the headers.
+
+ :param cache_timeout: the timeout in seconds for the headers. When `None`
+ (default), this value is set by
+ :meth:`~Flask.get_send_file_max_age` of
+ :data:`~flask.current_app`.
"""
mtime = None
if isinstance(filename_or_fp, basestring):
@@ -526,6 +529,8 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
rv.last_modified = int(mtime)
rv.cache_control.public = True
+ if cache_timeout is None:
+ cache_timeout = current_app.get_send_file_max_age(filename)
if cache_timeout:
rv.cache_control.max_age = cache_timeout
rv.expires = int(time() + cache_timeout)
@@ -782,26 +787,31 @@ def jinja_loader(self):
return FileSystemLoader(os.path.join(self.root_path,
self.template_folder))
- def get_send_file_options(self, filename):
- """Provides keyword arguments to send to :func:`send_from_directory`.
+ def get_send_file_max_age(self, filename):
+ """Provides default cache_timeout for the :func:`send_file` functions.
+
+ By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from
+ the configuration of :data:`~flask.current_app`.
+
+ Static file functions such as :func:`send_from_directory` use this
+ function, and :func:`send_file` calls this function on
+ :data:`~flask.current_app` when the given cache_timeout is `None`. If a
+ cache_timeout is given in :func:`send_file`, that timeout is used;
+ otherwise, this method is called.
This allows subclasses to change the behavior when sending files based
on the filename. For example, to set the cache timeout for .js files
- to 60 seconds (note the options are keywords for :func:`send_file`)::
+ to 60 seconds::
class MyFlask(flask.Flask):
- def get_send_file_options(self, filename):
- options = super(MyFlask, self).get_send_file_options(filename)
- if filename.lower().endswith('.js'):
- options['cache_timeout'] = 60
- options['conditional'] = True
- return options
+ def get_send_file_max_age(self, name):
+ if name.lower().endswith('.js'):
+ return 60
+ return flask.Flask.get_send_file_max_age(self, name)
.. versionadded:: 0.9
"""
- options = {}
- options['cache_timeout'] = current_app.config['SEND_FILE_MAX_AGE_DEFAULT']
- return options
+ return current_app.config['SEND_FILE_MAX_AGE_DEFAULT']
def send_static_file(self, filename):
"""Function used internally to send static files from the static
@@ -811,8 +821,11 @@ def send_static_file(self, filename):
"""
if not self.has_static_folder:
raise RuntimeError('No static folder for this object')
+ # Ensure get_send_file_max_age is called in all cases.
+ # Here, we ensure get_send_file_max_age is called for Blueprints.
+ cache_timeout = self.get_send_file_max_age(filename)
return send_from_directory(self.static_folder, filename,
- **self.get_send_file_options(filename))
+ cache_timeout=cache_timeout)
def open_resource(self, resource, mode='rb'):
"""Opens a resource from the application's resource folder. To see
View
10 flask/testsuite/appctx.py
@@ -65,6 +65,16 @@ def cleanup(exception):
self.assert_equal(cleanup_stuff, [None])
+ def test_custom_request_globals_class(self):
+ class CustomRequestGlobals(object):
+ def __init__(self):
+ self.spam = 'eggs'
+ app = flask.Flask(__name__)
+ app.request_globals_class = CustomRequestGlobals
+ with app.test_request_context():
+ self.assert_equal(
+ flask.render_template_string('{{ g.spam }}'), 'eggs')
+
def suite():
suite = unittest.TestSuite()
View
23 flask/testsuite/blueprints.py
@@ -386,6 +386,29 @@ def test_templates_and_static(self):
with flask.Flask(__name__).test_request_context():
self.assert_equal(flask.render_template('nested/nested.txt'), 'I\'m nested')
+ def test_default_static_cache_timeout(self):
+ app = flask.Flask(__name__)
+ class MyBlueprint(flask.Blueprint):
+ def get_send_file_max_age(self, filename):
+ return 100
+
+ blueprint = MyBlueprint('blueprint', __name__, static_folder='static')
+ app.register_blueprint(blueprint)
+
+ # try/finally, in case other tests use this app for Blueprint tests.
+ max_age_default = app.config['SEND_FILE_MAX_AGE_DEFAULT']
+ try:
+ with app.test_request_context():
+ unexpected_max_age = 3600
+ if app.config['SEND_FILE_MAX_AGE_DEFAULT'] == unexpected_max_age:
+ unexpected_max_age = 7200
+ app.config['SEND_FILE_MAX_AGE_DEFAULT'] = unexpected_max_age
+ rv = blueprint.send_static_file('index.html')
+ cc = parse_cache_control_header(rv.headers['Cache-Control'])
+ self.assert_equal(cc.max_age, 100)
+ finally:
+ app.config['SEND_FILE_MAX_AGE_DEFAULT'] = max_age_default
+
def test_templates_list(self):
from blueprintapp import app
templates = sorted(app.jinja_env.list_templates())
View
25 flask/testsuite/helpers.py
@@ -270,28 +270,37 @@ def test_static_file(self):
app = flask.Flask(__name__)
# default cache timeout is 12 hours
with app.test_request_context():
+ # Test with static file handler.
rv = app.send_static_file('index.html')
cc = parse_cache_control_header(rv.headers['Cache-Control'])
self.assert_equal(cc.max_age, 12 * 60 * 60)
+ # Test again with direct use of send_file utility.
+ rv = flask.send_file('static/index.html')
+ cc = parse_cache_control_header(rv.headers['Cache-Control'])
+ self.assert_equal(cc.max_age, 12 * 60 * 60)
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 3600
with app.test_request_context():
+ # Test with static file handler.
rv = app.send_static_file('index.html')
cc = parse_cache_control_header(rv.headers['Cache-Control'])
self.assert_equal(cc.max_age, 3600)
- # override get_send_file_options with some new values and check them
+ # Test again with direct use of send_file utility.
+ rv = flask.send_file('static/index.html')
+ cc = parse_cache_control_header(rv.headers['Cache-Control'])
+ self.assert_equal(cc.max_age, 3600)
class StaticFileApp(flask.Flask):
- def get_send_file_options(self, filename):
- opts = super(StaticFileApp, self).get_send_file_options(filename)
- opts['cache_timeout'] = 10
- # this test catches explicit inclusion of the conditional
- # keyword arg in the guts
- opts['conditional'] = True
- return opts
+ def get_send_file_max_age(self, filename):
+ return 10
app = StaticFileApp(__name__)
with app.test_request_context():
+ # Test with static file handler.
rv = app.send_static_file('index.html')
cc = parse_cache_control_header(rv.headers['Cache-Control'])
self.assert_equal(cc.max_age, 10)
+ # Test again with direct use of send_file utility.
+ rv = flask.send_file('static/index.html')
+ cc = parse_cache_control_header(rv.headers['Cache-Control'])
+ self.assert_equal(cc.max_age, 10)
class LoggingTestCase(FlaskTestCase):

0 comments on commit 3703156

Please sign in to comment.