diff --git a/CHANGES b/CHANGES
index a6fa7ec6..3fb30c6f 100644
--- a/CHANGES
+++ b/CHANGES
@@ -21,6 +21,10 @@ Version 1.0
- Added :attr:`flask.Flask.config_class`.
- Added :meth:`flask.config.Config.get_namespace`.
+- Added ``TEMPLATES_AUTO_RELOAD`` config key. If disabled the
+ templates will be reloaded only if the application is running in
+ debug mode. For higher performance it’s possible to disable that.
+
Version 0.10.2
--------------
diff --git a/docs/blueprints.rst b/docs/blueprints.rst
index 4e3888c2..8f83e120 100644
--- a/docs/blueprints.rst
+++ b/docs/blueprints.rst
@@ -202,3 +202,18 @@ you can use relative redirects by prefixing the endpoint with a dot only::
This will link to ``admin.index`` for instance in case the current request
was dispatched to any other admin blueprint endpoint.
+
+Error Handlers
+--------------
+
+Blueprints support the errorhandler decorator just like the :class:`Flask`
+application object, so it is easy to make Blueprint-specific custom error
+pages.
+
+Here is an example for a "404 Page Not Found" exception::
+
+ @simple_page.errorhandler(404)
+ def page_not_found(e):
+ return render_template('pages/404.html')
+
+More information on error handling see :ref:`errorpages`.
diff --git a/docs/config.rst b/docs/config.rst
index 85051e4a..d0e1bcdb 100644
--- a/docs/config.rst
+++ b/docs/config.rst
@@ -174,6 +174,12 @@ The following configuration values are used internally by Flask:
if they are not requested by an
XMLHttpRequest object (controlled by
the ``X-Requested-With`` header)
+``TEMPLATES_AUTO_RELOAD`` Flask checks if template was modified each
+ time it is requested and reloads it if
+ necessary. But disk I/O is costly and it may
+ be viable to disable this feature by setting
+ this key to ``False``. This option does not
+ affect debug mode.
================================= =========================================
.. admonition:: More on ``SERVER_NAME``
@@ -222,6 +228,9 @@ The following configuration values are used internally by Flask:
.. versionadded:: 1.0
``SESSION_REFRESH_EACH_REQUEST``
+.. versionadded:: 1.0
+ ``TEMPLATES_AUTO_RELOAD``
+
Configuring from Files
----------------------
diff --git a/docs/installation.rst b/docs/installation.rst
index 965e3733..78f192fd 100644
--- a/docs/installation.rst
+++ b/docs/installation.rst
@@ -3,7 +3,7 @@
Installation
============
-Flask depends on two external libraries, `Werkzeug
+Flask depends on some external libraries, like `Werkzeug
`_ and `Jinja2 `_.
Werkzeug is a toolkit for WSGI, the standard Python interface between web
applications and a variety of servers for both development and deployment.
@@ -13,7 +13,7 @@ So how do you get all that on your computer quickly? There are many ways you
could do that, but the most kick-ass method is virtualenv, so let's have a look
at that first.
-You will need Python 2.6 or higher to get started, so be sure to have an
+You will need Python 2.6 or newer to get started, so be sure to have an
up-to-date Python 2.x installation. For using Flask with Python 3 have a
look at :ref:`python3-support`.
@@ -67,7 +67,7 @@ folder within::
$ cd myproject
$ virtualenv venv
New python executable in venv/bin/python
- Installing distribute............done.
+ Installing setuptools, pip............done.
Now, whenever you want to work on a project, you only have to activate the
corresponding environment. On OS X and Linux, do the following::
@@ -113,9 +113,9 @@ Get the git checkout in a new virtualenv and run in development mode::
$ git clone http://github.com/mitsuhiko/flask.git
Initialized empty Git repository in ~/dev/flask/.git/
$ cd flask
- $ virtualenv venv --distribute
+ $ virtualenv venv
New python executable in venv/bin/python
- Installing distribute............done.
+ Installing setuptools, pip............done.
$ . venv/bin/activate
$ python setup.py develop
...
@@ -129,45 +129,53 @@ To just get the development version without git, do this instead::
$ mkdir flask
$ cd flask
- $ virtualenv venv --distribute
+ $ virtualenv venv
$ . venv/bin/activate
New python executable in venv/bin/python
- Installing distribute............done.
+ Installing setuptools, pip............done.
$ pip install Flask==dev
...
Finished processing dependencies for Flask==dev
.. _windows-easy-install:
-`pip` and `distribute` on Windows
------------------------------------
+`pip` and `setuptools` on Windows
+---------------------------------
+
+Sometimes getting the standard "Python packaging tools" like *pip*, *setuptools*
+and *virtualenv* can be a little trickier, but nothing very hard. The two crucial
+packages you will need are setuptools and pip - these will let you install
+anything else (like virtualenv). Fortunately there are two "bootstrap scripts"
+you can run to install either.
+
+If you don't currently have either, then `get-pip.py` will install both for you
+(you won't need to run ez_setup.py).
+
+`get-pip.py`_
-On Windows, installation of `easy_install` is a little bit trickier, but still
-quite easy. The easiest way to do it is to download the
-`distribute_setup.py`_ file and run it. The easiest way to run the file is to
-open your downloads folder and double-click on the file.
+To install the latest setuptools, you can use its bootstrap file:
-Next, add the `easy_install` command and other Python scripts to the
-command search path, by adding your Python installation's Scripts folder
-to the `PATH` environment variable. To do that, right-click on the
-"Computer" icon on the Desktop or in the Start menu, and choose "Properties".
-Then click on "Advanced System settings" (in Windows XP, click on the
-"Advanced" tab instead). Then click on the "Environment variables" button.
-Finally, double-click on the "Path" variable in the "System variables" section,
-and add the path of your Python interpreter's Scripts folder. Be sure to
-delimit it from existing values with a semicolon. Assuming you are using
-Python 2.7 on the default path, add the following value::
+`ez_setup.py`_
+Either should be double-clickable once you download them. If you already have pip,
+you can upgrade them by running::
- ;C:\Python27\Scripts
+ > pip install --upgrade pip setuptools
-And you are done! To check that it worked, open the Command Prompt and execute
-``easy_install``. If you have User Account Control enabled on Windows Vista or
-Windows 7, it should prompt you for administrator privileges.
+Most often, once you pull up a command prompt you want to be able to type ``pip``
+and ``python`` which will run those things, but this might not automatically happen
+on Windows, because it doesn't know where those executables are (give either a try!).
-Now that you have ``easy_install``, you can use it to install ``pip``::
+To fix this, you should be able to navigate to your Python install directory
+(e.g ``C:\Python27``), then go to ``Tools``, then ``Scripts``; then find the
+``win_add2path.py`` file and run that. Open a **new** Command Prompt and
+check that you can now just type ``python`` to bring up the interpreter.
- > easy_install pip
+Finally, to install `virtualenv`_, you can simply run::
+ > pip install virtualenv
+
+Then you can be off on your way following the installation instructions above.
-.. _distribute_setup.py: http://python-distribute.org/distribute_setup.py
+.. _get-pip.py: https://raw.github.com/pypa/pip/master/contrib/get-pip.py
+.. _ez_setup.py: https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py
diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst
index 07a762d8..3c4d9ce9 100644
--- a/docs/patterns/sqlalchemy.rst
+++ b/docs/patterns/sqlalchemy.rst
@@ -185,6 +185,8 @@ you basically only need the engine::
Then you can either declare the tables in your code like in the examples
above, or automatically load them::
+ from sqlalchemy import Table
+
users = Table('users', metadata, autoload=True)
To insert data you can use the `insert` method. We have to get a
diff --git a/docs/unicode.rst b/docs/unicode.rst
index 413ea84d..5aa6e25d 100644
--- a/docs/unicode.rst
+++ b/docs/unicode.rst
@@ -1,7 +1,7 @@
Unicode in Flask
================
-Flask like Jinja2 and Werkzeug is totally Unicode based when it comes to
+Flask, like Jinja2 and Werkzeug, is totally Unicode based when it comes to
text. Not only these libraries, also the majority of web related Python
libraries that deal with text. If you don't know Unicode so far, you
should probably read `The Absolute Minimum Every Software Developer
diff --git a/examples/flaskr/schema.sql b/examples/flaskr/schema.sql
index dbb06319..25b2cadd 100644
--- a/examples/flaskr/schema.sql
+++ b/examples/flaskr/schema.sql
@@ -2,5 +2,5 @@ drop table if exists entries;
create table entries (
id integer primary key autoincrement,
title text not null,
- text text not null
+ 'text' text not null
);
diff --git a/flask/app.py b/flask/app.py
index 2bff6af0..8d59fd30 100644
--- a/flask/app.py
+++ b/flask/app.py
@@ -305,6 +305,7 @@ class Flask(_PackageBoundObject):
'JSON_AS_ASCII': True,
'JSON_SORT_KEYS': True,
'JSONIFY_PRETTYPRINT_REGULAR': True,
+ 'TEMPLATES_AUTO_RELOAD': True,
})
#: The rule object to use for URL rules created. This is used by
@@ -655,10 +656,16 @@ class Flask(_PackageBoundObject):
this function to customize the behavior.
.. versionadded:: 0.5
+ .. versionchanged:: 1.0
+ ``Environment.auto_reload`` set in accordance with
+ ``TEMPLATES_AUTO_RELOAD`` configuration option.
"""
options = dict(self.jinja_options)
if 'autoescape' not in options:
options['autoescape'] = self.select_jinja_autoescape
+ if 'auto_reload' not in options:
+ options['auto_reload'] = self.debug \
+ or self.config['TEMPLATES_AUTO_RELOAD']
rv = Environment(self, **options)
rv.globals.update(
url_for=url_for,
@@ -1070,6 +1077,11 @@ class Flask(_PackageBoundObject):
The first `None` refers to the active blueprint. If the error
handler should be application wide `None` shall be used.
+ .. versionadded:: 0.7
+ Use :meth:`register_error_handler` instead of modifying
+ :attr:`error_handler_spec` directly, for application wide error
+ handlers.
+
.. versionadded:: 0.7
One can now additionally also register custom exception types
that do not necessarily have to be a subclass of the
diff --git a/flask/blueprints.py b/flask/blueprints.py
index 45faf2c5..4abc0ffa 100644
--- a/flask/blueprints.py
+++ b/flask/blueprints.py
@@ -399,3 +399,14 @@ class Blueprint(_PackageBoundObject):
self.name, code_or_exception, f))
return f
return decorator
+
+ def register_error_handler(self, code_or_exception, f):
+ """Non-decorator version of the :meth:`errorhandler` error attach
+ function, akin to the :meth:`~flask.Flask.register_error_handler`
+ application-wide function of the :class:`~flask.Flask` object but
+ for error handlers limited to this blueprint.
+
+ .. versionadded:: 0.11
+ """
+ self.record_once(lambda s: s.app._register_error_handler(
+ self.name, code_or_exception, f))
diff --git a/flask/helpers.py b/flask/helpers.py
index 7d688c61..4cced215 100644
--- a/flask/helpers.py
+++ b/flask/helpers.py
@@ -199,16 +199,16 @@ def url_for(endpoint, **values):
For more information, head over to the :ref:`Quickstart `.
To integrate applications, :class:`Flask` has a hook to intercept URL build
- errors through :attr:`Flask.build_error_handler`. The `url_for` function
- results in a :exc:`~werkzeug.routing.BuildError` when the current app does
- not have a URL for the given endpoint and values. When it does, the
- :data:`~flask.current_app` calls its :attr:`~Flask.build_error_handler` if
+ errors through :attr:`Flask.url_build_error_handlers`. The `url_for`
+ function results in a :exc:`~werkzeug.routing.BuildError` when the current
+ app does not have a URL for the given endpoint and values. When it does, the
+ :data:`~flask.current_app` calls its :attr:`~Flask.url_build_error_handlers` if
it is not `None`, which can return a string to use as the result of
`url_for` (instead of `url_for`'s default to raise the
:exc:`~werkzeug.routing.BuildError` exception) or re-raise the exception.
An example::
- def external_url_handler(error, endpoint, **values):
+ def external_url_handler(error, endpoint, values):
"Looks up an external URL when `url_for` cannot build a URL."
# This is an example of hooking the build_error_handler.
# Here, lookup_url is some utility function you've built
@@ -225,10 +225,10 @@ def url_for(endpoint, **values):
# url_for will use this result, instead of raising BuildError.
return url
- app.build_error_handler = external_url_handler
+ app.url_build_error_handlers.append(external_url_handler)
Here, `error` is the instance of :exc:`~werkzeug.routing.BuildError`, and
- `endpoint` and `**values` are the arguments passed into `url_for`. Note
+ `endpoint` and `values` are the arguments passed into `url_for`. Note
that this is for building URLs outside the current application, and not for
handling 404 NotFound errors.
diff --git a/flask/json.py b/flask/json.py
index e83f8b36..bdebf707 100644
--- a/flask/json.py
+++ b/flask/json.py
@@ -25,7 +25,7 @@ except ImportError:
from itsdangerous import json as _json
-# figure out if simplejson escapes slashes. This behavior was changed
+# Figure out if simplejson escapes slashes. This behavior was changed
# from one version to another without reason.
_slash_escape = '\\/' not in _json.dumps('/')
diff --git a/flask/sessions.py b/flask/sessions.py
index 598ba4e9..82ba3506 100644
--- a/flask/sessions.py
+++ b/flask/sessions.py
@@ -223,7 +223,7 @@ class SessionInterface(object):
def get_cookie_path(self, app):
"""Returns the path for which the cookie should be valid. The
- default implementation uses the value from the SESSION_COOKIE_PATH``
+ default implementation uses the value from the ``SESSION_COOKIE_PATH``
config var if it's set, and falls back to ``APPLICATION_ROOT`` or
uses ``/`` if it's `None`.
"""
diff --git a/flask/signals.py b/flask/signals.py
index ac71b8ce..e9f7239d 100644
--- a/flask/signals.py
+++ b/flask/signals.py
@@ -37,12 +37,12 @@ except ImportError:
temporarily_connected_to = connected_to = _fail
del _fail
-# the namespace for code signals. If you are not flask code, do
+# The namespace for code signals. If you are not flask code, do
# not put signals in here. Create your own namespace instead.
_signals = Namespace()
-# core signals. For usage examples grep the sourcecode or consult
+# Core signals. For usage examples grep the sourcecode or consult
# the API documentation in docs/api.rst as well as docs/signals.rst
template_rendered = _signals.signal('template-rendered')
request_started = _signals.signal('request-started')
diff --git a/flask/testsuite/blueprints.py b/flask/testsuite/blueprints.py
index 8a0e5a79..8b3e530c 100644
--- a/flask/testsuite/blueprints.py
+++ b/flask/testsuite/blueprints.py
@@ -296,6 +296,39 @@ class BlueprintTestCase(FlaskTestCase):
self.assert_equal(c.get('/backend-no').data, b'backend says no')
self.assert_equal(c.get('/what-is-a-sideend').data, b'application itself says no')
+ def test_blueprint_specific_user_error_handling(self):
+ class MyDecoratorException(Exception):
+ pass
+ class MyFunctionException(Exception):
+ pass
+
+ blue = flask.Blueprint('blue', __name__)
+
+ @blue.errorhandler(MyDecoratorException)
+ def my_decorator_exception_handler(e):
+ self.assert_true(isinstance(e, MyDecoratorException))
+ return 'boom'
+
+ def my_function_exception_handler(e):
+ self.assert_true(isinstance(e, MyFunctionException))
+ return 'bam'
+ blue.register_error_handler(MyFunctionException, my_function_exception_handler)
+
+ @blue.route('/decorator')
+ def blue_deco_test():
+ raise MyDecoratorException()
+ @blue.route('/function')
+ def blue_func_test():
+ raise MyFunctionException()
+
+ app = flask.Flask(__name__)
+ app.register_blueprint(blue)
+
+ c = app.test_client()
+
+ self.assert_equal(c.get('/decorator').data, b'boom')
+ self.assert_equal(c.get('/function').data, b'bam')
+
def test_blueprint_url_definitions(self):
bp = flask.Blueprint('test', __name__)
diff --git a/flask/testsuite/ext.py b/flask/testsuite/ext.py
index 3c75540e..5ec6ec63 100644
--- a/flask/testsuite/ext.py
+++ b/flask/testsuite/ext.py
@@ -125,7 +125,9 @@ class ExtImportHookTestCase(FlaskTestCase):
next = tb.tb_next.tb_next
if not PY2:
next = next.tb_next
- self.assert_in('flask_broken/__init__.py', next.tb_frame.f_code.co_filename)
+
+ import os.path
+ self.assert_in(os.path.join('flask_broken', '__init__.py'), next.tb_frame.f_code.co_filename)
def suite():
diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py
index d596a747..38c1ceef 100644
--- a/flask/testsuite/helpers.py
+++ b/flask/testsuite/helpers.py
@@ -269,7 +269,7 @@ class SendfileTestCase(FlaskTestCase):
app = flask.Flask(__name__)
with catch_warnings() as captured:
with app.test_request_context():
- f = open(os.path.join(app.root_path, 'static/index.html'))
+ f = open(os.path.join(app.root_path, 'static/index.html'), mode='rb')
rv = flask.send_file(f)
rv.direct_passthrough = False
with app.open_resource('static/index.html') as f:
diff --git a/flask/testsuite/templating.py b/flask/testsuite/templating.py
index 0f0ad5b4..8a6829dd 100644
--- a/flask/testsuite/templating.py
+++ b/flask/testsuite/templating.py
@@ -295,6 +295,14 @@ class TemplatingTestCase(FlaskTestCase):
rv = app.test_client().get('/')
self.assert_equal(rv.data, b'Jameson
')
+ def test_templates_auto_reload(self):
+ app = flask.Flask(__name__)
+ self.assert_true(app.config['TEMPLATES_AUTO_RELOAD'])
+ self.assert_true(app.jinja_env.auto_reload)
+ app = flask.Flask(__name__)
+ app.config['TEMPLATES_AUTO_RELOAD'] = False
+ self.assert_false(app.jinja_env.auto_reload)
+
def suite():
suite = unittest.TestSuite()
diff --git a/flask/views.py b/flask/views.py
index 607cb7a4..a216241a 100644
--- a/flask/views.py
+++ b/flask/views.py
@@ -60,7 +60,7 @@ class View(object):
#: view function is created the result is automatically decorated.
#:
#: .. versionadded:: 0.8
- decorators = []
+ decorators = ()
def dispatch_request(self):
"""Subclasses have to override this method to implement the
@@ -89,7 +89,7 @@ class View(object):
for decorator in cls.decorators:
view = decorator(view)
- # we attach the view class to the view function for two reasons:
+ # We attach the view class to the view function for two reasons:
# first of all it allows us to easily figure out what class-based
# view this thing came from, secondly it's also used for instantiating
# the view class so you can actually replace it with something else
@@ -111,7 +111,7 @@ class MethodViewType(type):
for key in d:
if key in http_method_funcs:
methods.add(key.upper())
- # if we have no method at all in there we don't want to
+ # If we have no method at all in there we don't want to
# add a method list. (This is for instance the case for
# the baseclass or another subclass of a base method view
# that does not introduce new methods).
@@ -141,8 +141,8 @@ class MethodView(with_metaclass(MethodViewType, View)):
"""
def dispatch_request(self, *args, **kwargs):
meth = getattr(self, request.method.lower(), None)
- # if the request method is HEAD and we don't have a handler for it
- # retry with GET
+ # If the request method is HEAD and we don't have a handler for it
+ # retry with GET.
if meth is None and request.method == 'HEAD':
meth = getattr(self, 'get', None)
assert meth is not None, 'Unimplemented method %r' % request.method
diff --git a/flask/wrappers.py b/flask/wrappers.py
index 1d6fbef5..528e94f5 100644
--- a/flask/wrappers.py
+++ b/flask/wrappers.py
@@ -40,25 +40,25 @@ class Request(RequestBase):
specific ones.
"""
- #: the internal URL rule that matched the request. This can be
+ #: The internal URL rule that matched the request. This can be
#: useful to inspect which methods are allowed for the URL from
#: a before/after handler (``request.url_rule.methods``) etc.
#:
#: .. versionadded:: 0.6
url_rule = None
- #: a dict of view arguments that matched the request. If an exception
+ #: A dict of view arguments that matched the request. If an exception
#: happened when matching, this will be `None`.
view_args = None
- #: if matching the URL failed, this is the exception that will be
+ #: If matching the URL failed, this is the exception that will be
#: raised / was raised as part of the request handling. This is
#: usually a :exc:`~werkzeug.exceptions.NotFound` exception or
#: something similar.
routing_exception = None
- # switched by the request context until 1.0 to opt in deprecated
- # module functionality
+ # Switched by the request context until 1.0 to opt in deprecated
+ # module functionality.
_is_old_module = False
@property
@@ -179,7 +179,7 @@ class Request(RequestBase):
def _load_form_data(self):
RequestBase._load_form_data(self)
- # in debug mode we're replacing the files multidict with an ad-hoc
+ # In debug mode we're replacing the files multidict with an ad-hoc
# subclass that raises a different error for key errors.
ctx = _request_ctx_stack.top
if ctx is not None and ctx.app.debug and \
diff --git a/scripts/make-release.py b/scripts/make-release.py
index d6375820..ed933239 100644
--- a/scripts/make-release.py
+++ b/scripts/make-release.py
@@ -26,7 +26,6 @@ def parse_changelog():
match = re.search('^Version\s+(.*)', line.strip())
if match is None:
continue
- length = len(match.group(1))
version = match.group(1).strip()
if lineiter.next().count('-') != len(match.group(0)):
continue
@@ -60,6 +59,7 @@ def parse_date(string):
def set_filename_version(filename, version_number, pattern):
changed = []
+
def inject_version(match):
before, old, after = match.groups()
changed.append(True)
@@ -133,7 +133,8 @@ def main():
if version in tags:
fail('Version "%s" is already tagged', version)
if release_date.date() != date.today():
- fail('Release date is not today (%s != %s)')
+ fail('Release date is not today (%s != %s)',
+ release_date.date(), date.today())
if not git_is_clean():
fail('You have uncommitted changes in git')