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 =