diff --git a/.travis.yml b/.travis.yml
index b9a4eb25..8d2d3c60 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -7,6 +7,7 @@ python:
- "pypy"
- "3.3"
- "3.4"
+ - "3.5"
env:
- REQUIREMENTS=lowest
@@ -20,6 +21,8 @@ matrix:
env: REQUIREMENTS=lowest
- python: "3.4"
env: REQUIREMENTS=lowest
+ - python: "3.5"
+ env: REQUIREMENTS=lowest
install:
diff --git a/CHANGES b/CHANGES
index 1727a794..e3d9b0eb 100644
--- a/CHANGES
+++ b/CHANGES
@@ -112,7 +112,7 @@ Version 0.10.1
Version 0.10
------------
-Released on June 13nd 2013, codename Limoncello.
+Released on June 13th 2013, codename Limoncello.
- Changed default cookie serialization format from pickle to JSON to
limit the impact an attacker can do if the secret key leaks. See
diff --git a/docs/config.rst b/docs/config.rst
index fb39b4c4..855136ff 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -130,7 +130,8 @@ The following configuration values are used internally by Flask:
``SEND_FILE_MAX_AGE_DEFAULT`` Default cache control max age to use with
:meth:`~flask.Flask.send_static_file` (the
default static file handler) and
- :func:`~flask.send_file`, in
+ :func:`~flask.send_file`, as
+ :class:`datetime.timedelta` or as seconds.
seconds. Override this value on a per-file
basis using the
:meth:`~flask.Flask.get_send_file_max_age`
diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst
index 272a9e27..ba506d6f 100644
--- a/docs/deploying/index.rst
+++ b/docs/deploying/index.rst
@@ -24,7 +24,7 @@ Hosted options
- `Deploying Flask on Webfaction `_
- `Deploying Flask on Google App Engine `_
- `Sharing your Localhost Server with Localtunnel `_
-
+- `Deploying on Azure (IIS) `_
Self-hosted options
-------------------
diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst
index 41b82c2b..c1ce170a 100644
--- a/docs/deploying/mod_wsgi.rst
+++ b/docs/deploying/mod_wsgi.rst
@@ -193,5 +193,11 @@ Add the following lines to the top of your ``.wsgi`` file::
activate_this = '/path/to/env/bin/activate_this.py'
execfile(activate_this, dict(__file__=activate_this))
+For Python 3 add the following lines to the top of your ``.wsgi`` file::
+
+ activate_this = '/path/to/env/bin/activate_this.py'
+ with open(activate_this) as file_:
+ exec(file_.read(), dict(__file__=activate_this))
+
This sets up the load paths according to the settings of the virtual
environment. Keep in mind that the path has to be absolute.
diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst
index cdf663a6..40e048e0 100644
--- a/docs/patterns/sqlalchemy.rst
+++ b/docs/patterns/sqlalchemy.rst
@@ -33,7 +33,7 @@ SQLAlchemy. It allows you to define tables and models in one go, similar
to how Django works. In addition to the following text I recommend the
official documentation on the `declarative`_ extension.
-Here the example :file:`database.py` module for your application::
+Here's the example :file:`database.py` module for your application::
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
diff --git a/flask/app.py b/flask/app.py
index 2d24d8b2..3d741ae9 100644
--- a/flask/app.py
+++ b/flask/app.py
@@ -246,6 +246,16 @@ class Flask(_PackageBoundObject):
permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME',
get_converter=_make_timedelta)
+ #: A :class:`~datetime.timedelta` which is used as default cache_timeout
+ #: for the :func:`send_file` functions. The default is 12 hours.
+ #:
+ #: This attribute can also be configured from the config with the
+ #: ``SEND_FILE_MAX_AGE_DEFAULT`` configuration key. This configuration
+ #: variable can also be set with an integer value used as seconds.
+ #: Defaults to ``timedelta(hours=12)``
+ send_file_max_age_default = ConfigAttribute('SEND_FILE_MAX_AGE_DEFAULT',
+ get_converter=_make_timedelta)
+
#: Enable this if you want to use the X-Sendfile feature. Keep in
#: mind that the server has to support this. This only affects files
#: sent with the :func:`send_file` method.
@@ -297,7 +307,7 @@ class Flask(_PackageBoundObject):
'SESSION_COOKIE_SECURE': False,
'SESSION_REFRESH_EACH_REQUEST': True,
'MAX_CONTENT_LENGTH': None,
- 'SEND_FILE_MAX_AGE_DEFAULT': 12 * 60 * 60, # 12 hours
+ 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12),
'TRAP_BAD_REQUEST_ERRORS': False,
'TRAP_HTTP_EXCEPTIONS': False,
'EXPLAIN_TEMPLATE_LOADING': False,
diff --git a/flask/helpers.py b/flask/helpers.py
index 861b21ab..479ca4bb 100644
--- a/flask/helpers.py
+++ b/flask/helpers.py
@@ -14,6 +14,7 @@ import sys
import pkgutil
import posixpath
import mimetypes
+from datetime import timedelta
from time import time
from zlib import adler32
from threading import RLock
@@ -856,7 +857,7 @@ class _PackageBoundObject(object):
.. versionadded:: 0.9
"""
- return current_app.config['SEND_FILE_MAX_AGE_DEFAULT']
+ return total_seconds(current_app.send_file_max_age_default)
def send_static_file(self, filename):
"""Function used internally to send static files from the static
@@ -898,3 +899,14 @@ class _PackageBoundObject(object):
if mode not in ('r', 'rb'):
raise ValueError('Resources can only be opened for reading')
return open(os.path.join(self.root_path, resource), mode)
+
+
+def total_seconds(td):
+ """Returns the total seconds from a timedelta object.
+
+ :param timedelta td: the timedelta to be converted in seconds
+
+ :returns: number of seconds
+ :rtype: int
+ """
+ return td.days * 60 * 60 * 24 + td.seconds
diff --git a/flask/sessions.py b/flask/sessions.py
index 1aa42f17..48fd08fa 100644
--- a/flask/sessions.py
+++ b/flask/sessions.py
@@ -17,14 +17,11 @@ from werkzeug.http import http_date, parse_date
from werkzeug.datastructures import CallbackDict
from . import Markup, json
from ._compat import iteritems, text_type
+from .helpers import total_seconds
from itsdangerous import URLSafeTimedSerializer, BadSignature
-def total_seconds(td):
- return td.days * 60 * 60 * 24 + td.seconds
-
-
class SessionMixin(object):
"""Expands a basic dictionary with an accessors that are expected
by Flask extensions and users for the session.
diff --git a/tests/test_config.py b/tests/test_config.py
index c16bb2f9..7a17b607 100644
--- a/tests/test_config.py
+++ b/tests/test_config.py
@@ -10,6 +10,7 @@
import pytest
import os
+from datetime import timedelta
import flask
@@ -168,6 +169,14 @@ def test_session_lifetime():
assert app.permanent_session_lifetime.seconds == 42
+def test_send_file_max_age():
+ app = flask.Flask(__name__)
+ app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 3600
+ assert app.send_file_max_age_default.seconds == 3600
+ app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(hours=2)
+ assert app.send_file_max_age_default.seconds == 7200
+
+
def test_get_namespace():
app = flask.Flask(__name__)
app.config['FOO_OPTION_1'] = 'foo option 1'
diff --git a/tox.ini b/tox.ini
index 3e170d87..83aed62f 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = {py26,py27,pypy}-{lowest,release,devel}, {py33,py34}-{release,devel}
+envlist = {py26,py27,pypy}-{lowest,release,devel}, {py33,py34,py35}-{release,devel}
[testenv]
commands =