Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Merge branch 'master' into fix-issue-220

  • Loading branch information...
commit 3703156d2ad779d6ff947800cb343e9822422a53 2 parents cf38eec + e78e2a1
authored May 01, 2012
19  CHANGES
@@ -55,18 +55,21 @@ Relase date to be decided, codename to be chosen.
55 55
 - View functions can now return a tuple with the first instance being an
56 56
   instance of :class:`flask.Response`.  This allows for returning
57 57
   ``jsonify(error="error msg"), 400`` from a view function.
58  
-- :class:`flask.Flask` now provides a `get_send_file_options` hook for
59  
-  subclasses to override behavior of serving static files from Flask when using
60  
-  :meth:`flask.Flask.send_static_file` based on keywords in
61  
-  :func:`flask.helpers.send_file`.  This hook is provided a filename, which for
62  
-  example allows changing cache controls by file extension.  The default
63  
-  max-age for `send_static_file` can be configured through a new
64  
-  ``SEND_FILE_MAX_AGE_DEFAULT`` configuration variable, regardless of whether
65  
-  the `get_send_file_options` hook is used.
  58
+- :class:`~flask.Flask` and :class:`~flask.Blueprint` now provide a
  59
+  :meth:`~flask.Flask.get_send_file_max_age` hook for subclasses to override
  60
+  behavior of serving static files from Flask when using
  61
+  :meth:`flask.Flask.send_static_file` (used for the default static file
  62
+  handler) and :func:`~flask.helpers.send_file`.  This hook is provided a
  63
+  filename, which for example allows changing cache controls by file extension.
  64
+  The default max-age for `send_file` and static files can be configured
  65
+  through a new ``SEND_FILE_MAX_AGE_DEFAULT`` configuration variable, which is
  66
+  used in the default `get_send_file_max_age` implementation.
66 67
 - Fixed an assumption in sessions implementation which could break message
67 68
   flashing on sessions implementations which use external storage.
68 69
 - Changed the behavior of tuple return values from functions.  They are no
69 70
   longer arguments to the response object, they now have a defined meaning.
  71
+- Added :attr:`flask.Flask.request_globals_class` to allow a specific class to
  72
+  be used on creation of the :data:`~flask.g` instance of each request.
70 73
 
71 74
 Version 0.8.1
72 75
 -------------
65  docs/advanced_foreword.rst
Source Rendered
... ...
@@ -1,27 +1,26 @@
  1
+.. _advanced_foreword:
  2
+
1 3
 Foreword for Experienced Programmers
2 4
 ====================================
3 5
 
4  
-This chapter is for programmers who have worked with other frameworks in the
5  
-past, and who may have more specific or esoteric concerns that the typical
6  
-user.
7  
-
8  
-Threads in Flask
9  
-----------------
  6
+Thread-Locals in Flask
  7
+----------------------
10 8
 
11  
-One of the design decisions with Flask was that simple tasks should be simple;
  9
+One of the design decisions in Flask was that simple tasks should be simple;
12 10
 they should not take a lot of code and yet they should not limit you. Because
13  
-of that we made a few design choices that some people might find surprising or
14  
-unorthodox. For example, Flask uses thread-local objects internally so that
15  
-you don’t have to pass objects around from function to function within a
16  
-request in order to stay threadsafe. While this is a really easy approach and
17  
-saves you a lot of time, it might also cause some troubles for very large
18  
-applications because changes on these thread-local objects can happen anywhere
19  
-in the same thread. In order to solve these problems we don’t hide the thread
20  
-locals for you but instead embrace them and provide you with a lot of tools to
21  
-make it as pleasant as possible to work with them.
  11
+of that, Flask has few design choices that some people might find surprising or
  12
+unorthodox. For example, Flask uses thread-local objects internally so that you
  13
+don’t have to pass objects around from function to function within a request in
  14
+order to stay threadsafe. This approach is convenient, but requires a valid
  15
+request context for dependency injection or when attempting to reuse code which
  16
+uses a value pegged to the request.  The Flask project is honest about
  17
+thread-locals, does not hide them, and calls out in the code and documentation
  18
+where they are used.
22 19
 
23  
-Web Development is Dangerous
24  
-----------------------------
  20
+Develop for the Web with Caution
  21
+--------------------------------
  22
+
  23
+Always keep security in mind when building web applications.
25 24
 
26 25
 If you write a web application, you are probably allowing users to register
27 26
 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.
30 29
 
31 30
 Unfortunately, there are many ways the security of a web application can be
32 31
 compromised.  Flask protects you against one of the most common security
33  
-problems of modern web applications: cross-site scripting (XSS).  Unless
34  
-you deliberately mark insecure HTML as secure, Flask and the underlying
35  
-Jinja2 template engine have you covered.  But there are many more ways to
36  
-cause security problems.
  32
+problems of modern web applications: cross-site scripting (XSS).  Unless you
  33
+deliberately mark insecure HTML as secure, Flask and the underlying Jinja2
  34
+template engine have you covered.  But there are many more ways to cause
  35
+security problems.
37 36
 
38  
-The documentation will warn you about aspects of web development that
39  
-require attention to security.  Some of these security concerns
40  
-are far more complex than one might think, and we all sometimes underestimate
41  
-the likelihood that a vulnerability will be exploited - until a clever
42  
-attacker figures out a way to exploit our applications.  And don't think
43  
-that your application is not important enough to attract an attacker.
44  
-Depending on the kind of attack, chances are that automated bots are
45  
-probing for ways to fill your database with spam, links to malicious
46  
-software, and the like.
  37
+The documentation will warn you about aspects of web development that require
  38
+attention to security.  Some of these security concerns are far more complex
  39
+than one might think, and we all sometimes underestimate the likelihood that a
  40
+vulnerability will be exploited - until a clever attacker figures out a way to
  41
+exploit our applications.  And don't think that your application is not
  42
+important enough to attract an attacker.  Depending on the kind of attack,
  43
+chances are that automated bots are probing for ways to fill your database with
  44
+spam, links to malicious software, and the like.
47 45
 
48  
-So always keep security in mind when doing web development.
  46
+Flask is no different from any other framework in that you the developer must
  47
+build with caution, watching for exploits when building to your requirements.
49 48
 
50 49
 The Status of Python 3
51 50
 ----------------------
@@ -65,3 +64,5 @@ using Python 2.6 and 2.7 with activated Python 3 warnings during
65 64
 development.  If you plan on upgrading to Python 3 in the near future we
66 65
 strongly recommend that you read `How to write forwards compatible
67 66
 Python code <http://lucumr.pocoo.org/2011/1/22/forwards-compatible-python/>`_.
  67
+
  68
+Continue to :ref:`installation` or the :ref:`quickstart`.
100  docs/becomingbig.rst
Source Rendered
@@ -3,45 +3,57 @@
3 3
 Becoming Big
4 4
 ============
5 5
 
6  
-Your application is becoming more and more complex?  If you suddenly
7  
-realize that Flask does things in a way that does not work out for your
8  
-application there are ways to deal with that.
9  
-
10  
-Flask is powered by Werkzeug and Jinja2, two libraries that are in use at
11  
-a number of large websites out there and all Flask does is bring those
12  
-two together.  Being a microframework Flask does not do much more than
13  
-combining existing libraries - there is not a lot of code involved.
14  
-What that means for large applications is that it's very easy to take the
15  
-code from Flask and put it into a new module within the applications and
16  
-expand on that.
17  
-
18  
-Flask is designed to be extended and modified in a couple of different
19  
-ways:
20  
-
21  
--   Flask extensions.  For a lot of reusable functionality you can create
22  
-    extensions.  For extensions a number of hooks exist throughout Flask
23  
-    with signals and callback functions.
24  
-
25  
--   Subclassing.  The majority of functionality can be changed by creating
26  
-    a new subclass of the :class:`~flask.Flask` class and overriding
27  
-    methods provided for this exact purpose.
28  
-
29  
--   Forking.  If nothing else works out you can just take the Flask
30  
-    codebase at a given point and copy/paste it into your application
31  
-    and change it.  Flask is designed with that in mind and makes this
32  
-    incredible easy.  You just have to take the package and copy it
33  
-    into your application's code and rename it (for example to
34  
-    `framework`).  Then you can start modifying the code in there.
35  
-
36  
-Why consider Forking?
  6
+Here are your options when growing your codebase or scaling your application.
  7
+
  8
+Read the Source.
  9
+----------------
  10
+
  11
+Flask started in part to demonstrate how to build your own framework on top of
  12
+existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it
  13
+developed, it became useful to a wide audience.  As you grow your codebase,
  14
+don't just use Flask -- understand it.  Read the source.  Flask's code is
  15
+written to be read; it's documentation published so you can use its internal
  16
+APIs.  Flask sticks to documented APIs in upstream libraries, and documents its
  17
+internal utilities so that you can find the hook points needed for your
  18
+project.
  19
+
  20
+Hook. Extend.
  21
+-------------
  22
+
  23
+The :ref:`api` docs are full of available overrides, hook points, and
  24
+:ref:`signals`. You can provide custom classes for things like the request and
  25
+response objects.  Dig deeper on the APIs you use, and look for the
  26
+customizations which are available out of the box in a Flask release.  Look for
  27
+ways in which your project can be refactored into a collection of utilities and
  28
+Flask extensions.  Explore the many extensions in the community, and look for
  29
+patterns to build your own extensions if you do not find the tools you need.
  30
+
  31
+Subclass.
  32
+---------
  33
+
  34
+The :class:`~flask.Flask` class has many methods designed for subclassing. You
  35
+can quickly add or customize behavior by subclassing :class:`~flask.Flask` (see
  36
+the linked method docs) and using that subclass wherever you instantiate an
  37
+application class. This works well with :ref:`app-factories`.
  38
+
  39
+Wrap with middleware.
37 40
 ---------------------
38 41
 
39  
-The majority of code of Flask is within Werkzeug and Jinja2.  These
40  
-libraries do the majority of the work.  Flask is just the paste that glues
41  
-those together.  For every project there is the point where the underlying
42  
-framework gets in the way (due to assumptions the original developers
43  
-had).  This is natural because if this would not be the case, the
44  
-framework would be a very complex system to begin with which causes a
  42
+The :ref:`app-dispatch` chapter shows in detail how to apply middleware. You
  43
+can introduce WSGI middleware to wrap your Flask instances and introduce fixes
  44
+and changes at the layer between your Flask application and your HTTP
  45
+server. Werkzeug includes several `middlewares
  46
+<http://werkzeug.pocoo.org/docs/middlewares/>`_.
  47
+
  48
+Fork.
  49
+-----
  50
+
  51
+If none of the above options work, fork Flask.  The majority of code of Flask
  52
+is within Werkzeug and Jinja2.  These libraries do the majority of the work.
  53
+Flask is just the paste that glues those together.  For every project there is
  54
+the point where the underlying framework gets in the way (due to assumptions
  55
+the original developers had).  This is natural because if this would not be the
  56
+case, the framework would be a very complex system to begin with which causes a
45 57
 steep learning curve and a lot of user frustration.
46 58
 
47 59
 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,
55 67
 depending on the number of changes.  Because of that, forking should be
56 68
 the very last resort.
57 69
 
58  
-Scaling like a Pro
59  
-------------------
  70
+Scale like a pro.
  71
+-----------------
60 72
 
61 73
 For many web applications the complexity of the code is less an issue than
62 74
 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
78 90
 processes to achieve concurrency which are all methods well supported by
79 91
 the underlying Werkzeug library.
80 92
 
81  
-Dialogue with the Community
  93
+Discuss with the community.
82 94
 ---------------------------
83 95
 
84  
-The Flask developers are very interested to keep everybody happy, so as
85  
-soon as you find an obstacle in your way, caused by Flask, don't hesitate
86  
-to contact the developers on the mailinglist or IRC channel.  The best way
87  
-for the Flask and Flask-extension developers to improve it for larger
  96
+The Flask developers keep the framework accessible to users with codebases big
  97
+and small. If you find an obstacle in your way, caused by Flask, don't hesitate
  98
+to contact the developers on the mailinglist or IRC channel.  The best way for
  99
+the Flask and Flask extension developers to improve the tools for larger
88 100
 applications is getting feedback from users.
11  docs/config.rst
Source Rendered
@@ -111,12 +111,15 @@ The following configuration values are used internally by Flask:
111 111
                                   content length greater than this by
112 112
                                   returning a 413 status code.
113 113
 ``SEND_FILE_MAX_AGE_DEFAULT``:    Default cache control max age to use with
114  
-                                  :meth:`flask.Flask.send_static_file`, in
  114
+                                  :meth:`~flask.Flask.send_static_file` (the
  115
+                                  default static file handler) and
  116
+                                  :func:`~flask.send_file`, in
115 117
                                   seconds. Override this value on a per-file
116 118
                                   basis using the
117  
-                                  :meth:`flask.Flask.get_send_file_options` and
118  
-                                  :meth:`flask.Blueprint.get_send_file_options`
119  
-                                  hooks. Defaults to 43200 (12 hours).
  119
+                                  :meth:`~flask.Flask.get_send_file_max_age`
  120
+                                  hook on :class:`~flask.Flask` or
  121
+                                  :class:`~flask.Blueprint`,
  122
+                                  respectively. Defaults to 43200 (12 hours).
120 123
 ``TRAP_HTTP_EXCEPTIONS``          If this is set to ``True`` Flask will
121 124
                                   not execute the error handlers of HTTP
122 125
                                   exceptions but instead treat the
1  docs/contents.rst.inc
@@ -9,6 +9,7 @@ instructions for web development with Flask.
9 9
    :maxdepth: 2
10 10
 
11 11
    foreword
  12
+   advanced_foreword
12 13
    installation
13 14
    quickstart
14 15
    tutorial/index
76  docs/foreword.rst
Source Rendered
@@ -8,48 +8,50 @@ should or should not be using it.
8 8
 What does "micro" mean?
9 9
 -----------------------
10 10
 
11  
-“Micro” does not mean that your whole web application has to fit into
12  
-a  single Python file (although it certainly can). Nor does it mean
13  
-that Flask is lacking in functionality. The "micro" in microframework
14  
-means Flask aims to keep the core simple but extensible. Flask won't make 
15  
-many decisions for you, such as what database to use. Those decisions that 
16  
-it does make, such as what templating engine to use, are easy to change. 
17  
-Everything else is up to you, so that Flask can be everything you need 
18  
-and nothing you don't.
  11
+“Micro” does not mean that your whole web application has to fit into a single
  12
+Python file, although it certainly can. Nor does it mean that Flask is lacking
  13
+in functionality. The "micro" in microframework means Flask aims to keep the
  14
+core simple but extensible. Flask won't make many decisions for you, such as
  15
+what database to use. Those decisions that it does make, such as what
  16
+templating engine to use, are easy to change.  Everything else is up to you, so
  17
+that Flask can be everything you need and nothing you don't.
19 18
 
20 19
 By default, Flask does not include a database abstraction layer, form
21 20
 validation or anything else where different libraries already exist that can
22  
-handle that. Instead, FLask extensions add such functionality to your
23  
-application as if it was implemented in Flask itself. Numerous extensions
  21
+handle that. Instead, Flask supports extensions to add such functionality to
  22
+your application as if it was implemented in Flask itself. Numerous extensions
24 23
 provide database integration, form validation, upload handling, various open
25  
-authentication technologies, and more. Flask may be "micro", but the
26  
-possibilities are endless.
  24
+authentication technologies, and more. Flask may be "micro", but it's ready for
  25
+production use on a variety of needs.
27 26
 
28  
-Convention over Configuration
  27
+Configuration and Conventions
29 28
 -----------------------------
30 29
 
31  
-Flask is based on convention over configuration, which means that many things
32  
-are preconfigured. For example, by convention templates and static files are
33  
-stored in subdirectories within the application's Python source tree. While
34  
-this can be changed you usually don't have to. We want to minimize the time
35  
-you need to spend in order to get up and running, without assuming things 
36  
-about your needs.
37  
-
38  
-Growing Up
39  
-----------
40  
-
41  
-Since Flask is based on a very solid foundation there is not a lot of code in
42  
-Flask itself.  As such it's easy to adapt even for large applications and we
43  
-are making sure that you can either configure it as much as possible by
44  
-subclassing things or by forking the entire codebase.  If you are interested
45  
-in that, check out the :ref:`becomingbig` chapter.
46  
-
47  
-If you are curious about the Flask design principles, head over to the section
48  
-about :ref:`design`.
49  
-
50  
-For the Stalwart and Wizened...
51  
--------------------------------
52  
-
53  
-If you're more curious about the minutiae of Flask's implementation, and
54  
-whether its structure is right for your needs, read the
  30
+Flask has many configuration values, with sensible defaults, and a few
  31
+conventions when getting started.  By convention templates and static files are
  32
+stored in subdirectories within the application's Python source tree, with the
  33
+names `templates` and `static` respectively. While this can be changed you
  34
+usually don't have to, especially when getting started.
  35
+
  36
+Growing with Flask
  37
+------------------
  38
+
  39
+Once you have Flask up and running, you'll find a variety of extensions
  40
+available in the community to integrate your project for production. The Flask
  41
+core team reviews extensions and ensures approved extensions do not break with
  42
+future releases.
  43
+
  44
+As your codebase grows, you are free to make the design decisions appropriate
  45
+for your project.  Flask will continue to provide a very simple glue layer to
  46
+the best that Python has to offer.  You can implement advanced patterns in
  47
+SQLAlchemy or another database tool, introduce non-relational data persistence
  48
+as appropriate, and take advantage of framework-agnostic tools built for WSGI,
  49
+the Python web interface.
  50
+
  51
+Flask includes many hooks to customize its behavior. Should you need more
  52
+customization, the Flask class is built for subclassing. If you are interested
  53
+in that, check out the :ref:`becomingbig` chapter.  If you are curious about
  54
+the Flask design principles, head over to the section about :ref:`design`.
  55
+
  56
+Continue to :ref:`installation`, the :ref:`quickstart`, or the
55 57
 :ref:`advanced_foreword`.
20  flask/app.py
@@ -28,7 +28,7 @@
28 28
     find_package, JSONEncoder
29 29
 from .wrappers import Request, Response
30 30
 from .config import ConfigAttribute, Config
31  
-from .ctx import RequestContext, AppContext
  31
+from .ctx import RequestContext, AppContext, _RequestGlobals
32 32
 from .globals import _request_ctx_stack, request
33 33
 from .sessions import SecureCookieSessionInterface
34 34
 from .module import blueprint_is_module
@@ -148,6 +148,18 @@ class Flask(_PackageBoundObject):
148 148
     #: :class:`~flask.Response` for more information.
149 149
     response_class = Response
150 150
 
  151
+    #: The class that is used for the :data:`~flask.g` instance.
  152
+    #:
  153
+    #: Example use cases for a custom class:
  154
+    #:
  155
+    #: 1. Store arbitrary attributes on flask.g.
  156
+    #: 2. Add a property for lazy per-request database connectors.
  157
+    #: 3. Return None instead of AttributeError on expected attributes.
  158
+    #: 4. Raise exception if an unexpected attr is set, a "controlled" flask.g.
  159
+    #:
  160
+    #: .. versionadded:: 0.9
  161
+    request_globals_class = _RequestGlobals
  162
+
151 163
     #: The debug flag.  Set this to `True` to enable debugging of the
152 164
     #: application.  In debug mode the debugger will kick in when an unhandled
153 165
     #: exception ocurrs and the integrated server will automatically reload
@@ -1073,12 +1085,6 @@ def _register_error_handler(self, key, code_or_exception, f):
1073 1085
             self.error_handler_spec.setdefault(key, {}).setdefault(None, []) \
1074 1086
                 .append((code_or_exception, f))
1075 1087
 
1076  
-    def get_send_file_options(self, filename):
1077  
-        # Override: Hooks in SEND_FILE_MAX_AGE_DEFAULT config.
1078  
-        options = super(Flask, self).get_send_file_options(filename)
1079  
-        options['cache_timeout'] = self.config['SEND_FILE_MAX_AGE_DEFAULT']
1080  
-        return options
1081  
-
1082 1088
     @setupmethod
1083 1089
     def template_filter(self, name=None):
1084 1090
         """A decorator that is used to register custom template filter.
3  flask/ctx.py
@@ -18,6 +18,7 @@
18 18
 
19 19
 
20 20
 class _RequestGlobals(object):
  21
+    """A plain object."""
21 22
     pass
22 23
 
23 24
 
@@ -139,7 +140,7 @@ def __init__(self, app, environ):
139 140
         self.app = app
140 141
         self.request = app.request_class(environ)
141 142
         self.url_adapter = app.create_url_adapter(self.request)
142  
-        self.g = _RequestGlobals()
  143
+        self.g = app.request_globals_class()
143 144
         self.flashes = None
144 145
         self.session = None
145 146
 
51  flask/helpers.py
@@ -409,7 +409,7 @@ def get_flashed_messages(with_categories=False, category_filter=[]):
409 409
 
410 410
 def send_file(filename_or_fp, mimetype=None, as_attachment=False,
411 411
               attachment_filename=None, add_etags=True,
412  
-              cache_timeout=60 * 60 * 12, conditional=False):
  412
+              cache_timeout=None, conditional=False):
413 413
     """Sends the contents of a file to the client.  This will use the
414 414
     most efficient method available and configured.  By default it will
415 415
     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,
423 423
     guessing requires a `filename` or an `attachment_filename` to be
424 424
     provided.
425 425
 
426  
-    Note `get_send_file_options` in :class:`flask.Flask` hooks the
427  
-    ``SEND_FILE_MAX_AGE_DEFAULT`` configuration variable to set the default
428  
-    cache_timeout.
429  
-
430 426
     Please never pass filenames to this function from user sources without
431 427
     checking them first.  Something like this is usually sufficient to
432 428
     avoid security problems::
@@ -446,6 +442,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
446 442
        able to, otherwise attach an etag yourself.  This functionality
447 443
        will be removed in Flask 1.0
448 444
 
  445
+    .. versionchanged:: 0.9
  446
+       cache_timeout pulls its default from application config, when None.
  447
+
449 448
     :param filename_or_fp: the filename of the file to send.  This is
450 449
                            relative to the :attr:`~Flask.root_path` if a
451 450
                            relative path is specified.
@@ -462,7 +461,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
462 461
                                 differs from the file's filename.
463 462
     :param add_etags: set to `False` to disable attaching of etags.
464 463
     :param conditional: set to `True` to enable conditional responses.
465  
-    :param cache_timeout: the timeout in seconds for the headers.
  464
+
  465
+    :param cache_timeout: the timeout in seconds for the headers. When `None`
  466
+                          (default), this value is set by
  467
+                          :meth:`~Flask.get_send_file_max_age` of
  468
+                          :data:`~flask.current_app`.
466 469
     """
467 470
     mtime = None
468 471
     if isinstance(filename_or_fp, basestring):
@@ -526,6 +529,8 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False,
526 529
         rv.last_modified = int(mtime)
527 530
 
528 531
     rv.cache_control.public = True
  532
+    if cache_timeout is None:
  533
+        cache_timeout = current_app.get_send_file_max_age(filename)
529 534
     if cache_timeout:
530 535
         rv.cache_control.max_age = cache_timeout
531 536
         rv.expires = int(time() + cache_timeout)
@@ -782,26 +787,31 @@ def jinja_loader(self):
782 787
             return FileSystemLoader(os.path.join(self.root_path,
783 788
                                                  self.template_folder))
784 789
 
785  
-    def get_send_file_options(self, filename):
786  
-        """Provides keyword arguments to send to :func:`send_from_directory`.
  790
+    def get_send_file_max_age(self, filename):
  791
+        """Provides default cache_timeout for the :func:`send_file` functions.
  792
+
  793
+        By default, this function returns ``SEND_FILE_MAX_AGE_DEFAULT`` from
  794
+        the configuration of :data:`~flask.current_app`.
  795
+
  796
+        Static file functions such as :func:`send_from_directory` use this
  797
+        function, and :func:`send_file` calls this function on
  798
+        :data:`~flask.current_app` when the given cache_timeout is `None`. If a
  799
+        cache_timeout is given in :func:`send_file`, that timeout is used;
  800
+        otherwise, this method is called.
787 801
 
788 802
         This allows subclasses to change the behavior when sending files based
789 803
         on the filename.  For example, to set the cache timeout for .js files
790  
-        to 60 seconds (note the options are keywords for :func:`send_file`)::
  804
+        to 60 seconds::
791 805
 
792 806
             class MyFlask(flask.Flask):
793  
-                def get_send_file_options(self, filename):
794  
-                    options = super(MyFlask, self).get_send_file_options(filename)
795  
-                    if filename.lower().endswith('.js'):
796  
-                        options['cache_timeout'] = 60
797  
-                        options['conditional'] = True
798  
-                    return options
  807
+                def get_send_file_max_age(self, name):
  808
+                    if name.lower().endswith('.js'):
  809
+                        return 60
  810
+                    return flask.Flask.get_send_file_max_age(self, name)
799 811
 
800 812
         .. versionadded:: 0.9
801 813
         """
802  
-        options = {}
803  
-        options['cache_timeout'] = current_app.config['SEND_FILE_MAX_AGE_DEFAULT']
804  
-        return options
  814
+        return current_app.config['SEND_FILE_MAX_AGE_DEFAULT']
805 815
 
806 816
     def send_static_file(self, filename):
807 817
         """Function used internally to send static files from the static
@@ -811,8 +821,11 @@ def send_static_file(self, filename):
811 821
         """
812 822
         if not self.has_static_folder:
813 823
             raise RuntimeError('No static folder for this object')
  824
+        # Ensure get_send_file_max_age is called in all cases.
  825
+        # Here, we ensure get_send_file_max_age is called for Blueprints.
  826
+        cache_timeout = self.get_send_file_max_age(filename)
814 827
         return send_from_directory(self.static_folder, filename,
815  
-                **self.get_send_file_options(filename))
  828
+                                   cache_timeout=cache_timeout)
816 829
 
817 830
     def open_resource(self, resource, mode='rb'):
818 831
         """Opens a resource from the application's resource folder.  To see
10  flask/testsuite/appctx.py
@@ -65,6 +65,16 @@ def cleanup(exception):
65 65
 
66 66
         self.assert_equal(cleanup_stuff, [None])
67 67
 
  68
+    def test_custom_request_globals_class(self):
  69
+        class CustomRequestGlobals(object):
  70
+            def __init__(self):
  71
+                self.spam = 'eggs'
  72
+        app = flask.Flask(__name__)
  73
+        app.request_globals_class = CustomRequestGlobals
  74
+        with app.test_request_context():
  75
+            self.assert_equal(
  76
+                flask.render_template_string('{{ g.spam }}'), 'eggs')
  77
+
68 78
 
69 79
 def suite():
70 80
     suite = unittest.TestSuite()
23  flask/testsuite/blueprints.py
@@ -386,6 +386,29 @@ def test_templates_and_static(self):
386 386
         with flask.Flask(__name__).test_request_context():
387 387
             self.assert_equal(flask.render_template('nested/nested.txt'), 'I\'m nested')
388 388
 
  389
+    def test_default_static_cache_timeout(self):
  390
+        app = flask.Flask(__name__)
  391
+        class MyBlueprint(flask.Blueprint):
  392
+            def get_send_file_max_age(self, filename):
  393
+                return 100
  394
+
  395
+        blueprint = MyBlueprint('blueprint', __name__, static_folder='static')
  396
+        app.register_blueprint(blueprint)
  397
+
  398
+        # try/finally, in case other tests use this app for Blueprint tests.
  399
+        max_age_default = app.config['SEND_FILE_MAX_AGE_DEFAULT']
  400
+        try:
  401
+            with app.test_request_context():
  402
+                unexpected_max_age = 3600
  403
+                if app.config['SEND_FILE_MAX_AGE_DEFAULT'] == unexpected_max_age:
  404
+                    unexpected_max_age = 7200
  405
+                app.config['SEND_FILE_MAX_AGE_DEFAULT'] = unexpected_max_age
  406
+                rv = blueprint.send_static_file('index.html')
  407
+                cc = parse_cache_control_header(rv.headers['Cache-Control'])
  408
+                self.assert_equal(cc.max_age, 100)
  409
+        finally:
  410
+            app.config['SEND_FILE_MAX_AGE_DEFAULT'] = max_age_default
  411
+
389 412
     def test_templates_list(self):
390 413
         from blueprintapp import app
391 414
         templates = sorted(app.jinja_env.list_templates())
25  flask/testsuite/helpers.py
@@ -270,28 +270,37 @@ def test_static_file(self):
270 270
         app = flask.Flask(__name__)
271 271
         # default cache timeout is 12 hours
272 272
         with app.test_request_context():
  273
+            # Test with static file handler.
273 274
             rv = app.send_static_file('index.html')
274 275
             cc = parse_cache_control_header(rv.headers['Cache-Control'])
275 276
             self.assert_equal(cc.max_age, 12 * 60 * 60)
  277
+            # Test again with direct use of send_file utility.
  278
+            rv = flask.send_file('static/index.html')
  279
+            cc = parse_cache_control_header(rv.headers['Cache-Control'])
  280
+            self.assert_equal(cc.max_age, 12 * 60 * 60)
276 281
         app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 3600
277 282
         with app.test_request_context():
  283
+            # Test with static file handler.
278 284
             rv = app.send_static_file('index.html')
279 285
             cc = parse_cache_control_header(rv.headers['Cache-Control'])
280 286
             self.assert_equal(cc.max_age, 3600)
281  
-        # override get_send_file_options with some new values and check them
  287
+            # Test again with direct use of send_file utility.
  288
+            rv = flask.send_file('static/index.html')
  289
+            cc = parse_cache_control_header(rv.headers['Cache-Control'])
  290
+            self.assert_equal(cc.max_age, 3600)
282 291
         class StaticFileApp(flask.Flask):
283  
-            def get_send_file_options(self, filename):
284  
-                opts = super(StaticFileApp, self).get_send_file_options(filename)
285  
-                opts['cache_timeout'] = 10
286  
-                # this test catches explicit inclusion of the conditional
287  
-                # keyword arg in the guts
288  
-                opts['conditional'] = True
289  
-                return opts
  292
+            def get_send_file_max_age(self, filename):
  293
+                return 10
290 294
         app = StaticFileApp(__name__)
291 295
         with app.test_request_context():
  296
+            # Test with static file handler.
292 297
             rv = app.send_static_file('index.html')
293 298
             cc = parse_cache_control_header(rv.headers['Cache-Control'])
294 299
             self.assert_equal(cc.max_age, 10)
  300
+            # Test again with direct use of send_file utility.
  301
+            rv = flask.send_file('static/index.html')
  302
+            cc = parse_cache_control_header(rv.headers['Cache-Control'])
  303
+            self.assert_equal(cc.max_age, 10)
295 304
 
296 305
 
297 306
 class LoggingTestCase(FlaskTestCase):

0 notes on commit 3703156

Please sign in to comment.
Something went wrong with that request. Please try again.