From 1e39871d681bd41451cf8694cd67fffe017fa522 Mon Sep 17 00:00:00 2001 From: Quentin Roy Date: Sat, 20 Oct 2012 18:18:42 +0200 Subject: [PATCH 0001/2502] Add back path=None argument into find_module. This fixes #618 and revert #524. --- scripts/flaskext_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/flaskext_compat.py b/scripts/flaskext_compat.py index 050fd7e0..cb0b436c 100644 --- a/scripts/flaskext_compat.py +++ b/scripts/flaskext_compat.py @@ -44,7 +44,7 @@ class ExtensionImporter(object): def install(self): sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self] - def find_module(self, fullname): + def find_module(self, fullname, path=None): if fullname.startswith(self.prefix): return self From b9355a7d5f79cfa06324a72cbaef4b9ec7a299d3 Mon Sep 17 00:00:00 2001 From: Steve Leonard Date: Thu, 4 Apr 2013 06:56:24 -0300 Subject: [PATCH 0002/2502] Mention register_error_handler in errorhandler doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The documentation for @errorhandler gives "app.error_handler_spec[None][404] = page_not_found" as an example for adding an error handler without the decorator.  However, register_error_handler appears to be the correct way to do this (added 0.7), and it eliminates the problems with modifying error_handler_spec directly. --- flask/app.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/flask/app.py b/flask/app.py index 373479f5..036903a3 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1065,6 +1065,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 From d94b1bec24f47e5997787f5ee762d8b2566d81b4 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 14 Jun 2013 09:54:27 +0100 Subject: [PATCH 0003/2502] In case we need a 0.10.2 --- flask/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/__init__.py b/flask/__init__.py index 3fd89084..c60f82ba 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.10.1' +__version__ = '0.10.2-dev' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. diff --git a/setup.py b/setup.py index 19d4675a..4391caf5 100644 --- a/setup.py +++ b/setup.py @@ -78,7 +78,7 @@ class run_audit(Command): setup( name='Flask', - version='0.10.1', + version='0.10.2-dev', url='http://github.com/mitsuhiko/flask/', license='BSD', author='Armin Ronacher', From 977e7660accc6b31bdd002b74f0a29b2846c84ba Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 14 Jun 2013 09:55:08 +0100 Subject: [PATCH 0004/2502] Master is 0.11-dev --- flask/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/__init__.py b/flask/__init__.py index c60f82ba..ad8a6abd 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.10.2-dev' +__version__ = '0.11-dev' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. diff --git a/setup.py b/setup.py index 4391caf5..5ddfe246 100644 --- a/setup.py +++ b/setup.py @@ -78,7 +78,7 @@ class run_audit(Command): setup( name='Flask', - version='0.10.2-dev', + version='0.11-dev', url='http://github.com/mitsuhiko/flask/', license='BSD', author='Armin Ronacher', From dfdcac22927f8027f393e66d78345106cdbbfae5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 14 Jun 2013 10:54:16 +0100 Subject: [PATCH 0005/2502] Fixed some embarrassing grammar --- flask/blueprints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/blueprints.py b/flask/blueprints.py index 4575ec9b..d45fd062 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -167,7 +167,7 @@ class Blueprint(_PackageBoundObject): the :func:`url_for` function is prefixed with the name of the blueprint. """ if endpoint: - assert '.' not in endpoint, "Blueprint endpoint's should not contain dot's" + assert '.' not in endpoint, "Blueprint endpoints should not contain dots" self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options)) From 582a5c7b89467b91662c9183c1b6d6b9d789e173 Mon Sep 17 00:00:00 2001 From: Stefano Costa Date: Fri, 14 Jun 2013 16:35:46 +0300 Subject: [PATCH 0006/2502] Correct small typo in internal link --- docs/advanced_foreword.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced_foreword.rst b/docs/advanced_foreword.rst index f7e70a71..53df8175 100644 --- a/docs/advanced_foreword.rst +++ b/docs/advanced_foreword.rst @@ -64,6 +64,6 @@ compatible Python code `_. If you do want to dive into Python 3 already have a look at the -:ref:`python3_support` page. +:ref:`python3-support` page. Continue to :ref:`installation` or the :ref:`quickstart`. From dda373823be9bfc69002ca4a01c2397ef5719141 Mon Sep 17 00:00:00 2001 From: augustusdsouza Date: Sat, 15 Jun 2013 00:01:34 +0530 Subject: [PATCH 0007/2502] Fixed a typo --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index fb0a511b..7e640c55 100644 --- a/CHANGES +++ b/CHANGES @@ -15,7 +15,7 @@ Version 0.10.1 - Added support for byte strings back to the session system. This broke compatibility with the common case of people putting binary data for token verification into the session. -- Fixed an issue were registering the same method twice for the same endpoint +- Fixed an issue where registering the same method twice for the same endpoint would trigger an exception incorrectly. Version 0.10 From 88b74b376e3781ac0204c5a22dcd08279a21722e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Wed, 19 Jun 2013 17:24:02 +0200 Subject: [PATCH 0008/2502] Don't use werkzeug dev version in tox anymore We don't need the dev version anymore since the lastest werkzeug release supports 3.3. --- tox.ini | 1 - 1 file changed, 1 deletion(-) diff --git a/tox.ini b/tox.ini index a8782e00..56c8f393 100644 --- a/tox.ini +++ b/tox.ini @@ -2,5 +2,4 @@ envlist = py26, py27, pypy, py33 [testenv] -deps = -egit+git://github.com/mitsuhiko/werkzeug.git#egg=werkzeug commands = python run-tests.py [] From ec5b182f15d711aa92fe16480011fbe0fb9d3a63 Mon Sep 17 00:00:00 2001 From: Karol Kuczmarski Date: Sat, 22 Jun 2013 22:09:30 +0200 Subject: [PATCH 0009/2502] Add Flask.config_class feature --- flask/app.py | 11 ++++++++++- flask/testsuite/__init__.py | 3 +++ flask/testsuite/config.py | 10 ++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index addc40b4..216afd8b 100644 --- a/flask/app.py +++ b/flask/app.py @@ -175,6 +175,15 @@ class Flask(_PackageBoundObject): _set_request_globals_class) del _get_request_globals_class, _set_request_globals_class + #: The class that is used for the ``config`` attribute of this app. + #: Defaults to :class:`~flask.Config`. + #: + #: Example use cases for a custom class: + #: + #: 1. Default values for certain config options. + #: 2. Access to config values through attributes in addition to keys. + config_class = Config + #: 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 occurs and the integrated server will automatically reload @@ -609,7 +618,7 @@ class Flask(_PackageBoundObject): root_path = self.root_path if instance_relative: root_path = self.instance_path - return Config(root_path, self.default_config) + return self.config_class(root_path, self.default_config) def auto_find_instance_path(self): """Tries to locate the instance path if it was not provided to the diff --git a/flask/testsuite/__init__.py b/flask/testsuite/__init__.py index 7fe61484..d6c4604e 100644 --- a/flask/testsuite/__init__.py +++ b/flask/testsuite/__init__.py @@ -157,6 +157,9 @@ class FlaskTestCase(unittest.TestCase): def assert_not_in(self, x, y): self.assertNotIn(x, y) + def assert_isinstance(self, obj, cls): + self.assertIsInstance(obj, cls) + if sys.version_info[:2] == (2, 6): def assertIn(self, x, y): assert x in y, "%r unexpectedly not in %r" % (x, y) diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py index 477c6db9..7d074c01 100644 --- a/flask/testsuite/config.py +++ b/flask/testsuite/config.py @@ -99,6 +99,16 @@ class ConfigTestCase(FlaskTestCase): self.assert_true(0, 'expected config') self.assert_false(app.config.from_pyfile('missing.cfg', silent=True)) + def test_custom_config_class(self): + class Config(flask.Config): + pass + class Flask(flask.Flask): + config_class = Config + app = Flask(__name__) + self.assert_isinstance(app.config, Config) + app.config.from_object(__name__) + self.common_object_test(app) + def test_session_lifetime(self): app = flask.Flask(__name__) app.config['PERMANENT_SESSION_LIFETIME'] = 42 From a225a304b822da292ed83f4c0ce28234a5d5ec5e Mon Sep 17 00:00:00 2001 From: Igor Mozharovsky Date: Sun, 23 Jun 2013 03:36:20 +0300 Subject: [PATCH 0010/2502] Fix typo --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 48bb001e..9662187f 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -370,7 +370,7 @@ JSON module: 1. ``datetime`` objects are serialized as :rfc:`822` strings. 2. Any object with an ``__html__`` method (like :class:`~flask.Markup`) - will ahve that method called and then the return value is serialized + will have that method called and then the return value is serialized as string. The :func:`~htmlsafe_dumps` function of this json module is also available From f88cc2d2f9d14d97e33ddd2bbaa4b1885db06e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Thu, 27 Jun 2013 16:49:27 +0200 Subject: [PATCH 0011/2502] Fix broken test_appcontext_signals test case This fixes #781 and ensures that Flask is tested with blinker installed. --- .travis-devel-requirements.txt | 3 +++ .travis-lowest-requirements.txt | 3 +++ .travis-release-requirements.txt | 2 ++ CHANGES | 7 +++++++ flask/testsuite/signals.py | 2 +- tox.ini | 1 + 6 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.travis-devel-requirements.txt b/.travis-devel-requirements.txt index afdc2649..c8bd9c45 100644 --- a/.travis-devel-requirements.txt +++ b/.travis-devel-requirements.txt @@ -1,3 +1,6 @@ git+git://github.com/mitsuhiko/werkzeug.git#egg=Werkzeug git+git://github.com/mitsuhiko/jinja2.git#egg=Jinja2 git+git://github.com/mitsuhiko/itsdangerous.git#egg=itsdangerous + +# extra dependencies +hg+http://bitbucket.org/jek/blinker#egg=blinker diff --git a/.travis-lowest-requirements.txt b/.travis-lowest-requirements.txt index 54498262..5066d698 100644 --- a/.travis-lowest-requirements.txt +++ b/.travis-lowest-requirements.txt @@ -1,3 +1,6 @@ Werkzeug==0.7 Jinja2==2.4 itsdangerous==0.21 + +# extra dependencies +blinker==1.0 diff --git a/.travis-release-requirements.txt b/.travis-release-requirements.txt index e69de29b..f2f097cb 100644 --- a/.travis-release-requirements.txt +++ b/.travis-release-requirements.txt @@ -0,0 +1,2 @@ +# extra dependencies +blinker diff --git a/CHANGES b/CHANGES index fb0a511b..89ef41d7 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,13 @@ Flask Changelog Here you can see the full list of changes between each Flask release. +Version 0.10.2 +-------------- + +(bugfix release, release date to be announced) + +- Fixed broken `test_appcontext_signals()` test case. + Version 0.10.1 -------------- diff --git a/flask/testsuite/signals.py b/flask/testsuite/signals.py index e061932d..45ca45d9 100644 --- a/flask/testsuite/signals.py +++ b/flask/testsuite/signals.py @@ -102,7 +102,7 @@ class SignalsTestCase(FlaskTestCase): def record_push(sender, **kwargs): recorded.append('push') def record_pop(sender, **kwargs): - recorded.append('push') + recorded.append('pop') @app.route('/') def index(): diff --git a/tox.ini b/tox.ini index a8782e00..711762fb 100644 --- a/tox.ini +++ b/tox.ini @@ -3,4 +3,5 @@ envlist = py26, py27, pypy, py33 [testenv] deps = -egit+git://github.com/mitsuhiko/werkzeug.git#egg=werkzeug + blinker commands = python run-tests.py [] From a364140bef85f9919fd3831d23ec6983cdf80be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Thu, 27 Jun 2013 17:08:02 +0200 Subject: [PATCH 0012/2502] Do not test with blinker dev version --- .travis-devel-requirements.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis-devel-requirements.txt b/.travis-devel-requirements.txt index c8bd9c45..0379f639 100644 --- a/.travis-devel-requirements.txt +++ b/.travis-devel-requirements.txt @@ -3,4 +3,6 @@ git+git://github.com/mitsuhiko/jinja2.git#egg=Jinja2 git+git://github.com/mitsuhiko/itsdangerous.git#egg=itsdangerous # extra dependencies -hg+http://bitbucket.org/jek/blinker#egg=blinker +# Python 3.x support in the development version of blinker is broken, see: +# https://bitbucket.org/jek/blinker/issue/8/python-3-compatiblity-broken-in-tip +# hg+http://bitbucket.org/jek/blinker#egg=blinker From e2fdf28e97695e9bcc4ea962bfcfefee70510e33 Mon Sep 17 00:00:00 2001 From: BobStevens Date: Thu, 27 Jun 2013 16:31:04 -0400 Subject: [PATCH 0013/2502] word change for clarity changed "200 error code" to "200 status code" --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index b455e070..19242c5d 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -667,7 +667,7 @@ About Responses The return value from a view function is automatically converted into a response object for you. If the return value is a string it's converted into a response object with the string as response body, an ``200 OK`` -error code and a ``text/html`` mimetype. The logic that Flask applies to +status code and a ``text/html`` mimetype. The logic that Flask applies to converting return values into response objects is as follows: 1. If a response object of the correct type is returned it's directly From 9fe209b497ca6f51ffbb0bfc9d0131240067d807 Mon Sep 17 00:00:00 2001 From: BobStevens Date: Thu, 27 Jun 2013 16:32:20 -0400 Subject: [PATCH 0014/2502] fixed typo --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 19242c5d..9d96b00a 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -666,7 +666,7 @@ About Responses The return value from a view function is automatically converted into a response object for you. If the return value is a string it's converted -into a response object with the string as response body, an ``200 OK`` +into a response object with the string as response body, a ``200 OK`` status code and a ``text/html`` mimetype. The logic that Flask applies to converting return values into response objects is as follows: From 4028e2395c3b559ca65db13fb1407c79ca79a4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Gait=C3=A1n?= Date: Sun, 30 Jun 2013 13:17:39 -0300 Subject: [PATCH 0015/2502] plural --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index b455e070..dbe720dc 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -318,7 +318,7 @@ Now the interesting part is that in HTML4 and XHTML1, the only methods a form can submit to the server are `GET` and `POST`. But with JavaScript and future HTML standards you can use the other methods as well. Furthermore HTTP has become quite popular lately and browsers are no longer the only -clients that are using HTTP. For instance, many revision control system +clients that are using HTTP. For instance, many revision control systems use it. .. _HTTP RFC: http://www.ietf.org/rfc/rfc2068.txt From 13b828379385d04d191fd7b8ae99b4353f9cfca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Wed, 3 Jul 2013 12:37:43 +0200 Subject: [PATCH 0016/2502] Test blinker development version again Apparently blinker isn't broken on tip anymore. --- .travis-devel-requirements.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis-devel-requirements.txt b/.travis-devel-requirements.txt index 0379f639..c8bd9c45 100644 --- a/.travis-devel-requirements.txt +++ b/.travis-devel-requirements.txt @@ -3,6 +3,4 @@ git+git://github.com/mitsuhiko/jinja2.git#egg=Jinja2 git+git://github.com/mitsuhiko/itsdangerous.git#egg=itsdangerous # extra dependencies -# Python 3.x support in the development version of blinker is broken, see: -# https://bitbucket.org/jek/blinker/issue/8/python-3-compatiblity-broken-in-tip -# hg+http://bitbucket.org/jek/blinker#egg=blinker +hg+http://bitbucket.org/jek/blinker#egg=blinker From f606a6d6a156fe87bb1c9a5f81675d00a225aa65 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Fri, 5 Jul 2013 23:18:13 +0200 Subject: [PATCH 0017/2502] blinker repository moved to github --- .travis-devel-requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis-devel-requirements.txt b/.travis-devel-requirements.txt index c8bd9c45..18ee00ec 100644 --- a/.travis-devel-requirements.txt +++ b/.travis-devel-requirements.txt @@ -3,4 +3,4 @@ git+git://github.com/mitsuhiko/jinja2.git#egg=Jinja2 git+git://github.com/mitsuhiko/itsdangerous.git#egg=itsdangerous # extra dependencies -hg+http://bitbucket.org/jek/blinker#egg=blinker +git+git://github.com/jek/blinker.git#egg=blinker From f632ba25e0406d2f0e374eb2e4637599a18bff77 Mon Sep 17 00:00:00 2001 From: Chris Rebert Date: Mon, 22 Jul 2013 02:12:17 -0700 Subject: [PATCH 0018/2502] api.rst: fix some spelling/typos --- docs/api.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 9662187f..75295b1e 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -365,7 +365,7 @@ You can instead just do this:: from flask import json For usage examples, read the :mod:`json` documentation in the standard -lirbary. The following extensions are by default applied to the stdlib's +library. The following extensions are by default applied to the stdlib's JSON module: 1. ``datetime`` objects are serialized as :rfc:`822` strings. @@ -505,7 +505,7 @@ Signals .. data:: signals_available - `True` if the signalling system is available. This is the case + `True` if the signaling system is available. This is the case when `blinker`_ is installed. .. data:: template_rendered @@ -728,7 +728,7 @@ some defaults to :meth:`~flask.Flask.add_url_rule` or general behavior: - `required_methods`: if this attribute is set, Flask will always add these methods when registering a URL rule even if the methods were - explicitly overriden in the ``route()`` call. + explicitly overridden in the ``route()`` call. Full example:: From 5ace060e5e68e5b8e176075b205caf25fec0c505 Mon Sep 17 00:00:00 2001 From: Nick Zaccardi Date: Mon, 22 Jul 2013 09:03:57 -0500 Subject: [PATCH 0019/2502] cleared up confusion for from_envvar --- docs/tutorial/setup.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 3a8fba33..4d3ccc91 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -37,15 +37,15 @@ string it will import it) and then look for all uppercase variables defined there. In our case, the configuration we just wrote a few lines of code above. You can also move that into a separate file. -Usually, it is a good idea to load a configuration from a configurable -file. This is what :meth:`~flask.Config.from_envvar` can do, replacing the -:meth:`~flask.Config.from_object` line above:: - +Usually, it is a good idea to load a separate, environment specific +configuration file. Flask allows you to import multiple configurations and it +will use the setting defined in the last import. This enables robust +configuration setups. :meth:`~flask.Config.from_envvar` can help achieve this. + app.config.from_envvar('FLASKR_SETTINGS', silent=True) -That way someone can set an environment variable called -:envvar:`FLASKR_SETTINGS` to specify a config file to be loaded which will then -override the default values. The silent switch just tells Flask to not complain +Simply define the environment variable :envvar:`FLASKR_SETTINGS` that points to +a config file to be loaded. The silent switch just tells Flask to not complain if no such environment key is set. The `secret_key` is needed to keep the client-side sessions secure. From e59e00576d68641ad88143598f3d97aa1d12ec1a Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 24 Jul 2013 21:48:28 +0200 Subject: [PATCH 0020/2502] Fixed a documentation typo --- docs/signals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/signals.rst b/docs/signals.rst index 799b5a91..5d574965 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -321,7 +321,7 @@ The following signals exist in Flask: .. versionadded:: 0.10 -.. data:: appcontext_popped +.. data:: flask.appcontext_popped This signal is sent when an application context is popped. The sender is the application. This usually falls in line with the From 73b4a52ca525dc2a671c6966ee3c403d848ba768 Mon Sep 17 00:00:00 2001 From: Eric Radman Date: Fri, 26 Jul 2013 15:07:40 -0400 Subject: [PATCH 0021/2502] Update example for Apache FCGI config to use worker processes using FastCgiServer, along with some notes about FastCgiExternalServer --- docs/deploying/fastcgi.rst | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst index 1e17fb24..daf98e9a 100644 --- a/docs/deploying/fastcgi.rst +++ b/docs/deploying/fastcgi.rst @@ -55,16 +55,37 @@ Configuring Apache ------------------ The example above is good enough for a basic Apache deployment but your `.fcgi` -file will appear in your application URL -e.g. example.com/yourapplication.fcgi/news/. There are few ways to configure -your application so that yourapplication.fcgi does not appear in the URL. A -preferable way is to use the ScriptAlias configuration directive:: +file will appear in your application URL e.g. +example.com/yourapplication.fcgi/news/. There are few ways to configure your +application so that yourapplication.fcgi does not appear in the URL. A +preferable way is to use the ScriptAlias and SetHandler configuration directives +to route requests to the FastCGI server. The following example uses +FastCgiServer to start 5 instances of the application which will handle all +incomming requests: + + LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so + + FastCgiServer /var/www/html/yourapplication/app.fcgi -idle-timeout 300 -processes 5 - ServerName example.com - ScriptAlias / /path/to/yourapplication.fcgi/ + ServerName webapp1.mydomain.com + DocumentRoot /var/www/html/yourapplication + + AddHandler fastcgi-script fcgi + ScriptAlias / /var/www/html/yourapplication/app.fcgi/ + + + SetHandler fastcgi-script + +These processes will be managed by Apache. If you're using an standalone FastCGI +server, you can use the FastCgiExternalServer directive instead. Note that in +the following the path is not real, it's simply used as an identifier to other +directives such as AliasMatch: + + FastCgiServer /var/www/html/yourapplication -host 127.0.0.1:3000 + If you cannot set ScriptAlias, for example on an shared web host, you can use WSGI middleware to remove yourapplication.fcgi from the URLs. Set .htaccess:: From c541f7e3eebaaa494c1af3a82b35d0cebdf542b7 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 26 Jul 2013 23:44:40 +0200 Subject: [PATCH 0022/2502] Fixed a typo --- docs/deploying/fastcgi.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst index daf98e9a..1ac1f505 100644 --- a/docs/deploying/fastcgi.rst +++ b/docs/deploying/fastcgi.rst @@ -61,7 +61,7 @@ application so that yourapplication.fcgi does not appear in the URL. A preferable way is to use the ScriptAlias and SetHandler configuration directives to route requests to the FastCGI server. The following example uses FastCgiServer to start 5 instances of the application which will handle all -incomming requests: +incoming requests: LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so From fd9bf8f37609f220f4829050eed94df208543c56 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 27 Jul 2013 01:20:42 +0200 Subject: [PATCH 0023/2502] Fix #782 -- /tmp/ in tutorial --- docs/tutorial/setup.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 3a8fba33..becb941c 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -25,6 +25,12 @@ In `flaskr.py`:: USERNAME = 'admin' PASSWORD = 'default' +.. admonition:: Windows + + If you are on Windows, replace `/tmp/flaskr.db` with a different writeable + path of your choice, in the configuration and for the rest of this + tutorial. + Next we can create our actual application and initialize it with the config from the same file, in `flaskr.py`:: From b6116c1de3f3556f6ae18d87348355d2545ce314 Mon Sep 17 00:00:00 2001 From: Robert Picard Date: Mon, 29 Jul 2013 13:42:04 -0400 Subject: [PATCH 0024/2502] Remove Werkzeug bug workaround from flask/app.py According to the note in the comment, you had to check to make sure that the defaults were not an empty dictionary because of a bug in Werkzeug pre-0.7. Since Flask officially requires 0.7 or greater, we can remove this little workaround. --- flask/app.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/flask/app.py b/flask/app.py index addc40b4..a08e76d8 100644 --- a/flask/app.py +++ b/flask/app.py @@ -968,11 +968,6 @@ class Flask(_PackageBoundObject): # Add the required methods now. methods |= required_methods - # due to a werkzeug bug we need to make sure that the defaults are - # None if they are an empty dictionary. This should not be necessary - # with Werkzeug 0.7 - options['defaults'] = options.get('defaults') or None - rule = self.url_rule_class(rule, methods=methods, **options) rule.provide_automatic_options = provide_automatic_options From 1a66a7e11005b93cb7b288ab5505290b5afa751a Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 30 Jul 2013 16:43:41 +0200 Subject: [PATCH 0025/2502] Added line for 1.0 --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index d2208969..fe3e2235 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,11 @@ Flask Changelog Here you can see the full list of changes between each Flask release. +Version 1.0 +----------- + +(release date to be announced, codename to be selected) + Version 0.10.2 -------------- From d1d835c02302884b2db1cab099b3ea6a84f41d32 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 30 Jul 2013 16:43:54 +0200 Subject: [PATCH 0026/2502] Added SESSION_REFRESH_EACH_REQUEST config option. This also changes how sessions are being refreshed. With the new behavior set-cookie is only emitted if the session is modified or if the session is permanent. Permanent sessions can be set to not refresh automatically through the SESSION_REFRESH_EACH_REQUEST config key. This fixes #798. --- CHANGES | 7 +++++++ docs/config.rst | 12 +++++++++++ flask/app.py | 1 + flask/sessions.py | 33 ++++++++++++++++++++++++++++++ flask/testsuite/basic.py | 43 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 96 insertions(+) diff --git a/CHANGES b/CHANGES index fe3e2235..912c6aaf 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,13 @@ Version 1.0 (release date to be announced, codename to be selected) +- Added ``SESSION_REFRESH_EACH_REQUEST`` config key that controls the + set-cookie behavior. If set to `True` a permanent session will be + refreshed each request and get their lifetime extended, if set to + `False` it will only be modified if the session actually modifies. + Non permanent sessions are not affected by this and will always + expire if the browser window closes. + Version 0.10.2 -------------- diff --git a/docs/config.rst b/docs/config.rst index ced2ad82..1bc46afa 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -88,6 +88,15 @@ The following configuration values are used internally by Flask: :class:`datetime.timedelta` object. Starting with Flask 0.8 this can also be an integer representing seconds. +``SESSION_REFRESH_EACH_REQUEST`` this flag controls how permanent + sessions are refresh. If set to `True` + (which is the default) then the cookie + is refreshed each request which + automatically bumps the lifetime. If + set to `False` a `set-cookie` header is + only sent if the session is modified. + Non permanent sessions are not affected + by this. ``USE_X_SENDFILE`` enable/disable x-sendfile ``LOGGER_NAME`` the name of the logger ``SERVER_NAME`` the name and port number of the server. @@ -210,6 +219,9 @@ The following configuration values are used internally by Flask: .. versionadded:: 0.10 ``JSON_AS_ASCII``, ``JSON_SORT_KEYS``, ``JSONIFY_PRETTYPRINT_REGULAR`` +.. versionadded:: 1.0 + ``SESSION_REFRESH_EACH_REQUEST`` + Configuring from Files ---------------------- diff --git a/flask/app.py b/flask/app.py index addc40b4..652c1809 100644 --- a/flask/app.py +++ b/flask/app.py @@ -285,6 +285,7 @@ class Flask(_PackageBoundObject): 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, + 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': 12 * 60 * 60, # 12 hours 'TRAP_BAD_REQUEST_ERRORS': False, diff --git a/flask/sessions.py b/flask/sessions.py index 3246eb83..d6b7e5ae 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -252,6 +252,24 @@ class SessionInterface(object): if session.permanent: return datetime.utcnow() + app.permanent_session_lifetime + def should_set_cookie(self, app, session): + """Indicates weather a cookie should be set now or not. This is + used by session backends to figure out if they should emit a + set-cookie header or not. The default behavior is controlled by + the ``SESSION_REFRESH_EACH_REQUEST`` config variable. If + it's set to `False` then a cookie is only set if the session is + modified, if set to `True` it's always set if the session is + permanent. + + This check is usually skipped if sessions get deleted. + + .. versionadded:: 1.0 + """ + if session.modified: + return True + save_each = app.config['SESSION_REFRESH_EACH_REQUEST'] + return save_each and session.permanent + def open_session(self, app, request): """This method has to be implemented and must either return `None` in case the loading failed because of a configuration error or an @@ -315,11 +333,26 @@ class SecureCookieSessionInterface(SessionInterface): def save_session(self, app, session, response): domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) + + # Delete case. If there is no session we bail early. + # If the session was modified to be empty we remove the + # whole cookie. if not session: if session.modified: response.delete_cookie(app.session_cookie_name, domain=domain, path=path) return + + # Modification case. There are upsides and downsides to + # emitting a set-cookie header each request. The behavior + # is controlled by the :meth:`should_set_cookie` method + # which performs a quick check to figure out if the cookie + # should be set or not. This is controlled by the + # SESSION_REFRESH_EACH_REQUEST config flag as well as + # the permanent flag on the session itself. + if not self.should_set_cookie(app, session): + return + httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) expires = self.get_expiration_time(app, session) diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 51fd46f2..0fffd4ee 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -345,6 +345,49 @@ class BasicFunctionalityTestCase(FlaskTestCase): self.assert_equal(type(rv['b']), bytes) self.assert_equal(rv['t'], (1, 2, 3)) + def test_session_cookie_setting(self): + app = flask.Flask(__name__) + app.testing = True + app.secret_key = 'dev key' + is_permanent = True + + @app.route('/bump') + def bump(): + rv = flask.session['foo'] = flask.session.get('foo', 0) + 1 + flask.session.permanent = is_permanent + return str(rv) + + @app.route('/read') + def read(): + return str(flask.session.get('foo', 0)) + + def run_test(expect_header): + with app.test_client() as c: + self.assert_equal(c.get('/bump').data, '1') + self.assert_equal(c.get('/bump').data, '2') + self.assert_equal(c.get('/bump').data, '3') + + rv = c.get('/read') + set_cookie = rv.headers.get('set-cookie') + self.assert_equal(set_cookie is not None, expect_header) + self.assert_equal(rv.data, '3') + + is_permanent = True + app.config['SESSION_REFRESH_EACH_REQUEST'] = True + run_test(expect_header=True) + + is_permanent = True + app.config['SESSION_REFRESH_EACH_REQUEST'] = False + run_test(expect_header=False) + + is_permanent = False + app.config['SESSION_REFRESH_EACH_REQUEST'] = True + run_test(expect_header=False) + + is_permanent = False + app.config['SESSION_REFRESH_EACH_REQUEST'] = False + run_test(expect_header=False) + def test_flashes(self): app = flask.Flask(__name__) app.secret_key = 'testkey' From 3e4dbf93d9ef2b64d32b291aa6f7620bdbc6576a Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 30 Jul 2013 16:56:40 +0200 Subject: [PATCH 0027/2502] Fixed a test that broke on 3.x --- flask/testsuite/basic.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 0fffd4ee..d95a4c37 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -363,14 +363,14 @@ class BasicFunctionalityTestCase(FlaskTestCase): def run_test(expect_header): with app.test_client() as c: - self.assert_equal(c.get('/bump').data, '1') - self.assert_equal(c.get('/bump').data, '2') - self.assert_equal(c.get('/bump').data, '3') + self.assert_equal(c.get('/bump').data, b'1') + self.assert_equal(c.get('/bump').data, b'2') + self.assert_equal(c.get('/bump').data, b'3') rv = c.get('/read') set_cookie = rv.headers.get('set-cookie') self.assert_equal(set_cookie is not None, expect_header) - self.assert_equal(rv.data, '3') + self.assert_equal(rv.data, b'3') is_permanent = True app.config['SESSION_REFRESH_EACH_REQUEST'] = True From af5a0853bfe36d547af93b904729a43fcf8cc55d Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Tue, 30 Jul 2013 22:35:53 +0200 Subject: [PATCH 0028/2502] Fix #815 --- flask/app.py | 3 +++ flask/testsuite/basic.py | 7 +++++++ 2 files changed, 10 insertions(+) diff --git a/flask/app.py b/flask/app.py index 1d2e356c..a837c030 100644 --- a/flask/app.py +++ b/flask/app.py @@ -949,6 +949,9 @@ class Flask(_PackageBoundObject): # a tuple of only `GET` as default. if methods is None: methods = getattr(view_func, 'methods', None) or ('GET',) + if isinstance(methods, string_types): + raise TypeError('Allowed methods have to be iterables of strings, ' + 'for example: @app.route(..., methods=["POST"])') methods = set(methods) # Methods that should always be added diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index d95a4c37..71a1f832 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -85,6 +85,13 @@ class BasicFunctionalityTestCase(FlaskTestCase): self.assert_equal(rv.status_code, 405) self.assert_equal(sorted(rv.allow), ['GET', 'HEAD', 'OPTIONS', 'POST']) + def test_disallow_string_for_allowed_methods(self): + app = flask.Flask(__name__) + with self.assert_raises(TypeError): + @app.route('/', methods='GET POST') + def index(): + return "Hey" + def test_url_mapping(self): app = flask.Flask(__name__) def index(): From 159ae3dc26248fa0c65f1990b11363c2cc009f54 Mon Sep 17 00:00:00 2001 From: Bill Mill Date: Wed, 31 Jul 2013 22:06:15 -0400 Subject: [PATCH 0029/2502] Fix doc bug, clarify use of make_dicts --- docs/patterns/sqlite3.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/patterns/sqlite3.rst b/docs/patterns/sqlite3.rst index f2e7eef3..660b1a0e 100644 --- a/docs/patterns/sqlite3.rst +++ b/docs/patterns/sqlite3.rst @@ -72,10 +72,10 @@ Now in each request handling function you can access `g.db` to get the current open database connection. To simplify working with SQLite, a row factory function is useful. It is executed for every result returned from the database to convert the result. For instance in order to get -dictionaries instead of tuples this can be used:: +dictionaries instead of tuples this could be inserted into ``get_db``:: def make_dicts(cursor, row): - return dict((cur.description[idx][0], value) + return dict((cursor.description[idx][0], value) for idx, value in enumerate(row)) db.row_factory = make_dicts From 5e8dd8b2872584483e7fb7506337a7d940ea8cc0 Mon Sep 17 00:00:00 2001 From: Chason Chaffin Date: Sun, 4 Aug 2013 01:42:23 -0700 Subject: [PATCH 0030/2502] Changed sort_json test to test str sorting Tests when simplejson was installed were failing because of a change in how it sorted in v3.0.0. This change first tests it via normal int sorting for stdlib json then if that fails, it tests against str sorting for simplejson. --- flask/testsuite/helpers.py | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index 636f67fa..7de70c0a 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -173,7 +173,33 @@ class JSONTestCase(FlaskTestCase): c = app.test_client() rv = c.get('/') lines = [x.strip() for x in rv.data.strip().decode('utf-8').splitlines()] - self.assert_equal(lines, [ + sorted_by_str = [ + '{', + '"values": {', + '"0": "foo",', + '"1": "foo",', + '"10": "foo",', + '"11": "foo",', + '"12": "foo",', + '"13": "foo",', + '"14": "foo",', + '"15": "foo",', + '"16": "foo",', + '"17": "foo",', + '"18": "foo",', + '"19": "foo",', + '"2": "foo",', + '"3": "foo",', + '"4": "foo",', + '"5": "foo",', + '"6": "foo",', + '"7": "foo",', + '"8": "foo",', + '"9": "foo"', + '}', + '}' + ] + sorted_by_int = [ '{', '"values": {', '"0": "foo",', @@ -198,8 +224,12 @@ class JSONTestCase(FlaskTestCase): '"19": "foo"', '}', '}' - ]) + ] + try: + self.assert_equal(lines, sorted_by_int) + except AssertionError: + self.assert_equal(lines, sorted_by_str) class SendfileTestCase(FlaskTestCase): From 9e5ab21c8e503a3e5a29b4ca77fd63663ec6e44a Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Sun, 4 Aug 2013 22:33:17 -0700 Subject: [PATCH 0031/2502] app.py: Link to correct EnvironBuilder docs --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index a837c030..805dc166 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1773,7 +1773,7 @@ class Flask(_PackageBoundObject): def test_request_context(self, *args, **kwargs): """Creates a WSGI environment from the given values (see - :func:`werkzeug.test.EnvironBuilder` for more information, this + :class:`werkzeug.test.EnvironBuilder` for more information, this function accepts the same arguments). """ from flask.testing import make_test_environ_builder From b290bf4079bc325d6b13f9043afb17c4aee48df0 Mon Sep 17 00:00:00 2001 From: Matt Wright Date: Wed, 7 Aug 2013 18:03:37 -0400 Subject: [PATCH 0032/2502] Add ability to config from a JSON file --- flask/config.py | 27 +++++++++++++++++++++++++++ flask/testsuite/config.py | 19 +++++++++++++++++++ flask/testsuite/static/config.json | 4 ++++ 3 files changed, 50 insertions(+) create mode 100644 flask/testsuite/static/config.json diff --git a/flask/config.py b/flask/config.py index 155afa2f..0d9f822f 100644 --- a/flask/config.py +++ b/flask/config.py @@ -15,6 +15,7 @@ import errno from werkzeug.utils import import_string from ._compat import string_types +from . import json class ConfigAttribute(object): @@ -164,5 +165,31 @@ class Config(dict): if key.isupper(): self[key] = getattr(obj, key) + def from_json(self, filename, silent=False): + """Updates the values in the config from a JSON file. This function + behaves as if the JSON object was a dictionary and passed ot the + :meth:`from_object` function. + + :param filename: the filename of the JSON file. This can either be an + absolute filename or a filename relative to the + root path. + :param silent: set to `True` if you want silent failure for missing + files. + """ + filename = os.path.join(self.root_path, filename) + + try: + with open(filename) as json_file: + obj = json.loads(json_file.read()) + except IOError as e: + if silent and e.errno in (errno.ENOENT, errno.EISDIR): + return False + e.strerror = 'Unable to load configuration file (%s)' % e.strerror + raise + for key in obj.keys(): + if key.isupper(): + self[key] = obj[key] + return True + def __repr__(self): return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self)) diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py index 477c6db9..7a9f574c 100644 --- a/flask/testsuite/config.py +++ b/flask/testsuite/config.py @@ -40,6 +40,12 @@ class ConfigTestCase(FlaskTestCase): app.config.from_object(__name__) self.common_object_test(app) + def test_config_from_json(self): + app = flask.Flask(__name__) + current_dir = os.path.dirname(os.path.abspath(__file__)) + app.config.from_json(os.path.join(current_dir, 'static', 'config.json')) + self.common_object_test(app) + def test_config_from_class(self): class Base(object): TEST_KEY = 'foo' @@ -99,6 +105,19 @@ class ConfigTestCase(FlaskTestCase): self.assert_true(0, 'expected config') self.assert_false(app.config.from_pyfile('missing.cfg', silent=True)) + def test_config_missing_json(self): + app = flask.Flask(__name__) + try: + app.config.from_json('missing.json') + except IOError as e: + msg = str(e) + self.assert_true(msg.startswith('[Errno 2] Unable to load configuration ' + 'file (No such file or directory):')) + self.assert_true(msg.endswith("missing.json'")) + else: + self.assert_true(0, 'expected config') + self.assert_false(app.config.from_json('missing.json', silent=True)) + def test_session_lifetime(self): app = flask.Flask(__name__) app.config['PERMANENT_SESSION_LIFETIME'] = 42 diff --git a/flask/testsuite/static/config.json b/flask/testsuite/static/config.json new file mode 100644 index 00000000..4a9722ec --- /dev/null +++ b/flask/testsuite/static/config.json @@ -0,0 +1,4 @@ +{ + "TEST_KEY": "foo", + "SECRET_KEY": "devkey" +} From 63a126c66a047507f634348688dfcf760b14d5ae Mon Sep 17 00:00:00 2001 From: Jamie Grove Date: Sat, 10 Aug 2013 11:35:47 -0400 Subject: [PATCH 0033/2502] Update blueprints.rst - Error Handlers Added information about custom error handlers in Blueprints with example based simple_page. --- docs/blueprints.rst | 15 +++++++++++++++ 1 file changed, 15 insertions(+) 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`. From 8246ce6a15d31a6d36d673a4f39be548fd5a3262 Mon Sep 17 00:00:00 2001 From: Jochen Kupperschmidt Date: Sun, 11 Aug 2013 15:59:46 +0200 Subject: [PATCH 0034/2502] Fixed tiny but distracting typo. --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 75295b1e..5faccc30 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -350,7 +350,7 @@ JSON Support Flask uses ``simplejson`` for the JSON implementation. Since simplejson is provided both by the standard library as well as extension Flask will try simplejson first and then fall back to the stdlib json module. On top -of that it will delegate access to the current application's JSOn encoders +of that it will delegate access to the current application's JSON encoders and decoders for easier customization. So for starters instead of doing:: From 94f436013792249a9bb7206c2868d4c6c21fd887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Tue, 13 Aug 2013 15:53:58 +0200 Subject: [PATCH 0035/2502] Explain is_package AttributeError in find_package When a PEP 302 import hook is used that doesn't implement .is_package() an AttributeError is raised. This looks like a bug in Flask. This change fixes that problem in the sense that it explains, that the AttributeError is intentional. --- CHANGES | 3 +++ flask/helpers.py | 9 +++++++-- flask/testsuite/config.py | 13 +++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 89ef41d7..9e23bcfb 100644 --- a/CHANGES +++ b/CHANGES @@ -9,6 +9,9 @@ Version 0.10.2 (bugfix release, release date to be announced) - Fixed broken `test_appcontext_signals()` test case. +- Raise an :exc:`AttributeError` in :func:`flask.helpers.find_package` with a + useful message explaining why it is raised when a PEP 302 import hook is used + without an `is_package()` method. Version 0.10.1 -------------- diff --git a/flask/helpers.py b/flask/helpers.py index 1e7c87f0..e59f7d3c 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -679,8 +679,13 @@ def find_package(import_name): filename = sys.modules[import_name].__file__ package_path = os.path.abspath(os.path.dirname(filename)) # package_path ends with __init__.py for a package - if loader.is_package(root_mod_name): - package_path = os.path.dirname(package_path) + if hasattr(loader, 'is_package'): + if loader.is_package(root_mod_name): + package_path = os.path.dirname(package_path) + else: + raise AttributeError( + ('%s.is_package() method is missing but is ' + 'required by Flask of PEP 302 import hooks') % loader.__class__.__name__) site_parent, site_folder = os.path.split(package_path) py_prefix = os.path.abspath(sys.prefix) diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py index 477c6db9..13e14d85 100644 --- a/flask/testsuite/config.py +++ b/flask/testsuite/config.py @@ -16,6 +16,7 @@ import pkgutil import unittest from contextlib import contextmanager from flask.testsuite import FlaskTestCase +from flask._compat import PY2 # config keys used for the ConfigTestCase @@ -291,6 +292,18 @@ class InstanceTestCase(FlaskTestCase): if 'site_egg' in sys.modules: del sys.modules['site_egg'] + if PY2: + def test_meta_path_loader_without_is_package(self): + class Loader(object): + def find_module(self, name): + return self + sys.meta_path.append(Loader()) + try: + with self.assert_raises(AttributeError): + flask.Flask(__name__) + finally: + sys.meta_path.pop() + def suite(): suite = unittest.TestSuite() From f5d38dc4fcdf1d8edb3e42c67b6e0e1127cd00c7 Mon Sep 17 00:00:00 2001 From: enkore Date: Sat, 17 Aug 2013 21:32:52 +0200 Subject: [PATCH 0036/2502] Update sessions.py get_cookie_path: fix docstring --- flask/sessions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/sessions.py b/flask/sessions.py index d6b7e5ae..cdebe4b0 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`. """ From 17e5fb365d7bde8fc795311bb37fe6358e258dc5 Mon Sep 17 00:00:00 2001 From: Maximilian Hils Date: Mon, 19 Aug 2013 10:56:08 +0200 Subject: [PATCH 0037/2502] Fix typo in docstring --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index 805dc166..ef2af73c 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1547,7 +1547,7 @@ class Flask(_PackageBoundObject): :class:`tuple` A tuple in the form ``(response, status, headers)`` where `response` is any of the types defined here, `status` is a string - or an integer and `headers` is a list of + or an integer and `headers` is a list or a dictionary with header values. ======================= =========================================== From 3e90de2e10cfde5803fab87768430cffff7c78a0 Mon Sep 17 00:00:00 2001 From: Will Bowlin Date: Fri, 23 Aug 2013 00:22:57 -0500 Subject: [PATCH 0038/2502] corrected documentation for Request get_json() --- flask/wrappers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index 1a17824a..0efb44da 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -115,8 +115,8 @@ class Request(RequestBase): but this can be overriden by the `force` parameter. :param force: if set to `True` the mimetype is ignored. - :param silent: if set to `False` this method will fail silently - and return `False`. + :param silent: if set to `True` this method will fail silently + and return `None`. :param cache: if set to `True` the parsed JSON data is remembered on the request. """ From 9cff681f97849029d9ca0047fc999d899ae2fd53 Mon Sep 17 00:00:00 2001 From: Adam Obeng Date: Sat, 24 Aug 2013 21:21:18 -0400 Subject: [PATCH 0039/2502] Fix typo --- docs/deploying/fastcgi.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst index 1ac1f505..430a9748 100644 --- a/docs/deploying/fastcgi.rst +++ b/docs/deploying/fastcgi.rst @@ -149,6 +149,7 @@ A basic FastCGI configuration for lighttpd looks like that:: url.rewrite-once = ( "^(/static($|/.*))$" => "$1", "^(/.*)$" => "/yourapplication.fcgi$1" + ) Remember to enable the FastCGI, alias and rewrite modules. This configuration binds the application to `/yourapplication`. If you want the application to From 42cf782ee36a7cebb4b499a15e344017fede2c38 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 30 Aug 2013 19:50:45 +0600 Subject: [PATCH 0040/2502] Removed g object from example as it was unused --- examples/flaskr/flaskr.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index b193e94e..04645efa 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -11,7 +11,7 @@ """ from sqlite3 import dbapi2 as sqlite3 -from flask import Flask, request, session, g, redirect, url_for, abort, \ +from flask import Flask, request, session, redirect, url_for, abort, \ render_template, flash, _app_ctx_stack # configuration From 05161d35844ba7cc1f46c08a51de632b5f3c5269 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 1 Sep 2013 01:32:41 +0600 Subject: [PATCH 0041/2502] Rewrote tutorial to use the g based appcontext object --- docs/index.rst | 1 + docs/tutorial/dbcon.rst | 90 +++++++++++++++++++++++---------------- docs/tutorial/dbinit.rst | 49 +++++++++++---------- docs/tutorial/index.rst | 2 +- docs/tutorial/setup.rst | 70 +++++++++++++++++------------- docs/tutorial/views.rst | 16 +++---- examples/flaskr/flaskr.py | 44 ++++++++++--------- 7 files changed, 154 insertions(+), 118 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index ef57e07c..43702409 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -24,6 +24,7 @@ following links: - `Jinja2 Documentation `_ - `Werkzeug Documentation `_ + .. _Jinja2: http://jinja.pocoo.org/2/ .. _Werkzeug: http://werkzeug.pocoo.org/ diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst index 837983e4..55526fbf 100644 --- a/docs/tutorial/dbcon.rst +++ b/docs/tutorial/dbcon.rst @@ -1,56 +1,72 @@ .. _tutorial-dbcon: -Step 4: Request Database Connections ------------------------------------- +Step 3: Database Connections +---------------------------- -Now we know how we can open database connections and use them for scripts, -but how can we elegantly do that for requests? We will need the database -connection in all our functions so it makes sense to initialize them -before each request and shut them down afterwards. +We have created a function for establishing a database connection with +`create_db` but by itself that's not particularly useful. Creating and +closing database connections all the time is very inefficient, so we want +to keep it around for longer. Because database connections encapsulate a +transaction we also need to make sure that only one request at the time +uses the connection. So how can we elegantly do that with Flask? -Flask allows us to do that with the :meth:`~flask.Flask.before_request`, -:meth:`~flask.Flask.after_request` and :meth:`~flask.Flask.teardown_request` -decorators:: +This is where the application context comes into play. So let's start +there. - @app.before_request - def before_request(): - g.db = connect_db() +Flask provides us with two contexts: the application context and the +request context. For the time being all you have to know is that there +are special variables that use these. For instance the +:data:`~flask.request` variable is the request object associated with +the current request, whereas :data:`~flask.g` is a general purpose +variable associated with the current application context. We will go into +the details of this a bit later. - @app.teardown_request - def teardown_request(exception): - db = getattr(g, 'db', None) - if db is not None: - db.close() +For the time being all you have to know is that you can store information +savely on the :data:`~flask.g` object. -Functions marked with :meth:`~flask.Flask.before_request` are called before -a request and passed no arguments. Functions marked with -:meth:`~flask.Flask.after_request` are called after a request and -passed the response that will be sent to the client. They have to return -that response object or a different one. They are however not guaranteed -to be executed if an exception is raised, this is where functions marked with -:meth:`~flask.Flask.teardown_request` come in. They get called after the -response has been constructed. They are not allowed to modify the request, and -their return values are ignored. If an exception occurred while the request was -being processed, it is passed to each function; otherwise, `None` is passed in. +So when do you put it on there? To do that you can make a helper +function. The first time the function is called it will create a database +connection for the current context and successive calls will return the +already established connection:: -We store our current database connection on the special :data:`~flask.g` -object that Flask provides for us. This object stores information for one -request only and is available from within each function. Never store such -things on other objects because this would not work with threaded -environments. That special :data:`~flask.g` object does some magic behind -the scenes to ensure it does the right thing. + def get_db(): + """Opens a new database connection if there is none yet for the + current application context. + """ + if not hasattr(g, 'sqlite_db'): + g.sqlite_db = connect_db() + return g.sqlite_db -For an even better way to handle such resources see the :ref:`sqlite3` -documentation. -Continue to :ref:`tutorial-views`. +So now we know how to connect, but how do we properly disconnect? For +that flask provides us with the :meth:`~flask.Flask.teardown_appcontext` +decorator. It's executed every time the application context tears down:: + + @app.teardown_appcontext + def close_db(error): + """Closes the database again at the end of the request.""" + if hasattr(g, 'sqlite_db'): + g.sqlite_db.close() + +Functions marked with :meth:`~flask.Flask.teardown_appcontext` are called +every time the app context tears down. So what does this mean? +Essentially the app context is created before the request comes in and is +destroyed (teared down) whenever the request finishes. A teardown can +happen because of two reasons: either everything went well (the error +parameter will be `None`) or an exception happend in which case the error +is passed to the teardown function. + +Curious about what these contexts mean? Have a look at the +:ref:`app-context` documentation to learn more. + +Continue to :ref:`tutorial-dbinit`. .. hint:: Where do I put this code? If you've been following along in this tutorial, you might be wondering where to put the code from this step and the next. A logical place is to group these module-level functions together, and put your new - ``before_request`` and ``teardown_request`` functions below your existing + ``get_db`` and ``close_db`` functions below your existing ``init_db`` function (following the tutorial line-by-line). If you need a moment to find your bearings, take a look at how the `example diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 1241193a..cd128159 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -1,6 +1,6 @@ .. _tutorial-dbinit: -Step 3: Creating The Database +Step 4: Creating The Database ============================= Flaskr is a database powered application as outlined earlier, and more @@ -20,36 +20,39 @@ to provide the path to the database there which leaves some place for errors. It's a good idea to add a function that initializes the database for you to the application. -If you want to do that, you first have to import the -:func:`contextlib.closing` function from the contextlib package. -Accordingly, add the following lines to your existing imports in `flaskr.py`:: - - from contextlib import closing - -Next we can create a function called `init_db` that initializes the -database. For this we can use the `connect_db` function we defined -earlier. Just add that function below the `connect_db` function in -`flaskr.py`:: +To do this we can create a function called `init_db` that initializes the +database. Let me show you the code first. Just add that function below +the `connect_db` function in `flaskr.py`:: def init_db(): - with closing(connect_db()) as db: + app app.app_context(): + db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() -The :func:`~contextlib.closing` helper function allows us to keep a -connection open for the duration of the `with` block. The -:func:`~flask.Flask.open_resource` method of the application object -supports that functionality out of the box, so it can be used in the -`with` block directly. This function opens a file from the resource +So what's happening here? Remember how we learned last chapter that the +application context is created every time a request comes in? Here we +don't have a request yet, so we need to create the application context by +hand. Without an application context the :data:`~flask.g` object does not +know yet to which application it becomes as there could be more than one! + +The ``with app.app_context()`` statement establishes the application +context for us. In the body of the with statement the :flask:`~flask.g` +object will be associated with ``app``. At the end of the with statement +the association is released and all teardown functions are executed. This +means that our database connection is disconnected after the commit. + +The :func:`~flask.Flask.open_resource` method of the application object +is a convenient helper function that will open a resource that the +application provides. This function opens a file from the resource location (your `flaskr` folder) and allows you to read from it. We are using this here to execute a script on the database connection. -When we connect to a database we get a connection object (here called -`db`) that can give us a cursor. On that cursor there is a method to -execute a complete script. Finally we only have to commit the changes. -SQLite 3 and other transactional databases will not commit unless you -explicitly tell it to. +The connection object provided by SQLite can give us a cursor object. +On that cursor there is a method to execute a complete script. Finally we +only have to commit the changes. SQLite 3 and other transactional +databases will not commit unless you explicitly tell it to. Now it is possible to create a database by starting up a Python shell and importing and calling that function:: @@ -63,4 +66,4 @@ importing and calling that function:: you did call the `init_db` function and that your table names are correct (singular vs. plural for example). -Continue with :ref:`tutorial-dbcon` +Continue with :ref:`tutorial-views` diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index 3f2d659e..da37cf7a 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -24,8 +24,8 @@ the `example source`_. folders schema setup - dbinit dbcon + dbinit views templates css diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 4c5d1e49..5b059c56 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -5,31 +5,18 @@ Step 2: Application Setup Code Now that we have the schema in place we can create the application module. Let's call it `flaskr.py` inside the `flaskr` folder. For starters we -will add the imports we will need as well as the config section. For -small applications it's a possibility to drop the configuration directly -into the module which we will be doing here. However a cleaner solution -would be to create a separate `.ini` or `.py` file and load that or import -the values from there. +will add the imports and create the application object. For small +applications it's a possibility to drop the configuration directly into +the module which we will be doing here. However a cleaner solution would +be to create a separate `.ini` or `.py` file and load that or import the +values from there. -In `flaskr.py`:: +First we add the imports in `flaskr.py`:: # all the imports import sqlite3 - from flask import Flask, request, session, g, redirect, url_for, \ - abort, render_template, flash - - # configuration - DATABASE = '/tmp/flaskr.db' - DEBUG = True - SECRET_KEY = 'development key' - USERNAME = 'admin' - PASSWORD = 'default' - -.. admonition:: Windows - - If you are on Windows, replace `/tmp/flaskr.db` with a different writeable - path of your choice, in the configuration and for the rest of this - tutorial. + from flask import Flask, request, session, g, redirect, url_for, abort, \ + render_template, flash Next we can create our actual application and initialize it with the config from the same file, in `flaskr.py`:: @@ -38,10 +25,24 @@ config from the same file, in `flaskr.py`:: app = Flask(__name__) app.config.from_object(__name__) -:meth:`~flask.Config.from_object` will look at the given object (if it's a -string it will import it) and then look for all uppercase variables -defined there. In our case, the configuration we just wrote a few lines -of code above. You can also move that into a separate file. + # Load default config and override config from an environment variable + app.config.update(dict( + DATABASE='/tmp/flaskr.db', + DEBUG=True, + SECRET_KEY='development key', + USERNAME='admin', + PASSWORD='default' + )) + app.config.from_envvar('FLASKR_SETTINGS', silent=True) + +The :class:`~flask.Config` object works similar to a dictionary so we +can update it with new values. + +.. admonition:: Windows + + If you are on Windows, replace `/tmp/flaskr.db` with a different writeable + path of your choice, in the configuration and for the rest of this + tutorial. Usually, it is a good idea to load a separate, environment specific configuration file. Flask allows you to import multiple configurations and it @@ -54,7 +55,12 @@ Simply define the environment variable :envvar:`FLASKR_SETTINGS` that points to a config file to be loaded. The silent switch just tells Flask to not complain if no such environment key is set. -The `secret_key` is needed to keep the client-side sessions secure. +In addition to that you can use the :meth:`~flask.Config.from_object` +method on the config object and provide it with an import name of a +module. Flask will the initialize the variable from that module. Note +that in all cases only variable names that are uppercase are considered. + +The ``SECRET_KEY`` is needed to keep the client-side sessions secure. Choose that key wisely and as hard to guess and complex as possible. The debug flag enables or disables the interactive debugger. *Never leave debug mode activated in a production system*, because it will allow users to @@ -62,12 +68,18 @@ execute code on the server! We also add a method to easily connect to the database specified. That can be used to open a connection on request and also from the interactive -Python shell or a script. This will come in handy later. +Python shell or a script. This will come in handy later. We create a +simple database connection through SQLite and then tell it to use the +:class:`sqlite3.Row` object to represent rows. This allows us to treat +the rows as if they were dictionaries instead of tuples. :: def connect_db(): - return sqlite3.connect(app.config['DATABASE']) + """Connects to the specific database.""" + rv = sqlite3.connect(app.config['DATABASE']) + rv.row_factory = sqlite3.Row + return rv Finally we just add a line to the bottom of the file that fires up the server if we want to run that file as a standalone application:: @@ -93,4 +105,4 @@ focus on that a little later. First we should get the database working. :ref:`externally visible server ` section for more information. -Continue with :ref:`tutorial-dbinit`. +Continue with :ref:`tutorial-dbcon`. diff --git a/docs/tutorial/views.rst b/docs/tutorial/views.rst index 93bec3bf..69c1c161 100644 --- a/docs/tutorial/views.rst +++ b/docs/tutorial/views.rst @@ -12,18 +12,17 @@ Show Entries This view shows all the entries stored in the database. It listens on the root of the application and will select title and text from the database. The one with the highest id (the newest entry) will be on top. The rows -returned from the cursor are tuples with the columns ordered like specified -in the select statement. This is good enough for small applications like -here, but you might want to convert them into a dict. If you are -interested in how to do that, check out the :ref:`easy-querying` example. +returned from the cursor look a bit like tuples because we are using +the :class:`sqlite3.Row` row factory. The view function will pass the entries as dicts to the `show_entries.html` template and return the rendered one:: @app.route('/') def show_entries(): - cur = g.db.execute('select title, text from entries order by id desc') - entries = [dict(title=row[0], text=row[1]) for row in cur.fetchall()] + db = get_db() + cur = db.execute('select title, text from entries order by id desc') + entries = cur.fetchall() return render_template('show_entries.html', entries=entries) Add New Entry @@ -39,9 +38,10 @@ redirect back to the `show_entries` page:: def add_entry(): if not session.get('logged_in'): abort(401) - g.db.execute('insert into entries (title, text) values (?, ?)', + db = get_db() + db.execute('insert into entries (title, text) values (?, ?)', [request.form['title'], request.form['text']]) - g.db.commit() + db.commit() flash('New entry was successfully posted') return redirect(url_for('show_entries')) diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index 04645efa..6c613279 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -11,22 +11,31 @@ """ from sqlite3 import dbapi2 as sqlite3 -from flask import Flask, request, session, redirect, url_for, abort, \ - render_template, flash, _app_ctx_stack +from flask import Flask, request, session, g, redirect, url_for, abort, \ + render_template, flash -# configuration -DATABASE = '/tmp/flaskr.db' -DEBUG = True -SECRET_KEY = 'development key' -USERNAME = 'admin' -PASSWORD = 'default' # create our little application :) app = Flask(__name__) -app.config.from_object(__name__) + +# Load default config and override config from an environment variable +app.config.update(dict( + DATABASE='/tmp/flaskr.db', + DEBUG=True, + SECRET_KEY='development key', + USERNAME='admin', + PASSWORD='default' +)) app.config.from_envvar('FLASKR_SETTINGS', silent=True) +def connect_db(): + """Connects to the specific database.""" + rv = sqlite3.connect(app.config['DATABASE']) + rv.row_factory = sqlite3.Row + return rv + + def init_db(): """Creates the database tables.""" with app.app_context(): @@ -40,21 +49,16 @@ def get_db(): """Opens a new database connection if there is none yet for the current application context. """ - top = _app_ctx_stack.top - if not hasattr(top, 'sqlite_db'): - sqlite_db = sqlite3.connect(app.config['DATABASE']) - sqlite_db.row_factory = sqlite3.Row - top.sqlite_db = sqlite_db - - return top.sqlite_db + if not hasattr(g, 'sqlite_db'): + g.sqlite_db = connect_db() + return g.sqlite_db @app.teardown_appcontext -def close_db_connection(exception): +def close_db(error): """Closes the database again at the end of the request.""" - top = _app_ctx_stack.top - if hasattr(top, 'sqlite_db'): - top.sqlite_db.close() + if hasattr(g, 'sqlite_db'): + g.sqlite_db.close() @app.route('/') From 8f1dada542d9e9cc9af0eada3e99eb7ceda2e873 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 2 Sep 2013 04:57:01 +0600 Subject: [PATCH 0042/2502] Some cleanups --- flask/app.py | 3 +-- flask/debughelpers.py | 2 +- flask/json.py | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/flask/app.py b/flask/app.py index ef2af73c..1ea82fe7 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1158,7 +1158,6 @@ class Flask(_PackageBoundObject): """ self.jinja_env.tests[name or f.__name__] = f - @setupmethod def template_global(self, name=None): """A decorator that is used to register a custom template global function. @@ -1710,7 +1709,7 @@ class Flask(_PackageBoundObject): if bp is not None and bp in self.teardown_request_funcs: funcs = chain(funcs, reversed(self.teardown_request_funcs[bp])) for func in funcs: - rv = func(exc) + func(exc) request_tearing_down.send(self, exc=exc) def do_teardown_appcontext(self, exc=None): diff --git a/flask/debughelpers.py b/flask/debughelpers.py index 2f8510f9..019f0d7e 100644 --- a/flask/debughelpers.py +++ b/flask/debughelpers.py @@ -78,7 +78,7 @@ def attach_enctype_error_multidict(request): def __getitem__(self, key): try: return oldcls.__getitem__(self, key) - except KeyError as e: + except KeyError: if key not in request.form: raise raise DebugFilesKeyError(request, key) diff --git a/flask/json.py b/flask/json.py index 45ba3240..81c6deec 100644 --- a/flask/json.py +++ b/flask/json.py @@ -232,7 +232,7 @@ def jsonify(*args, **kwargs): """ indent = None if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] \ - and not request.is_xhr: + and not request.is_xhr: indent = 2 return current_app.response_class(dumps(dict(*args, **kwargs), indent=indent), From 5207c69064898f513c0b08110514f3fd39e5a4a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Tue, 3 Sep 2013 19:47:00 +0200 Subject: [PATCH 0043/2502] Fix #856 ommited typo in quickstart --- docs/quickstart.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index bb08b007..3cb9b2f7 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -192,8 +192,8 @@ The following converters exist: with a trailing slash will produce a 404 "Not Found" error. This behavior allows relative URLs to continue working even if the trailing - slash is ommited, consistent with how Apache and other servers work. Also, - the URLs will stay unique, which helps search engines avoid indexing the + slash is omitted, consistent with how Apache and other servers work. Also, + the URLs will stay unique, which helps search engines avoid indexing the same page twice. From 12d6ec413083606761264753f6216111ca38db8c Mon Sep 17 00:00:00 2001 From: Sean Vieira Date: Tue, 3 Sep 2013 23:36:10 -0400 Subject: [PATCH 0044/2502] Fix with block --- docs/tutorial/dbinit.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index cd128159..8f64c3b5 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -25,7 +25,7 @@ database. Let me show you the code first. Just add that function below the `connect_db` function in `flaskr.py`:: def init_db(): - app app.app_context(): + with app.app_context(): db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) From b03181363bd78ab0934af3badd9b4845e454fbb3 Mon Sep 17 00:00:00 2001 From: Wouter Van Hemel Date: Wed, 4 Sep 2013 12:15:32 +0300 Subject: [PATCH 0045/2502] Add a non-decorator version of the error handler register function The main application object has a register_error_handler function which mirrors the decorator's functionality. According to the principle of least surprise, make sure blueprints also have this convenience function. --- flask/blueprints.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/flask/blueprints.py b/flask/blueprints.py index d45fd062..2b64c1f9 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)) From 079ae20f24e23ba4ab32faeca88705b247fd3eb2 Mon Sep 17 00:00:00 2001 From: Wouter Van Hemel Date: Fri, 6 Sep 2013 10:43:02 +0300 Subject: [PATCH 0046/2502] Add tests for user-defined exceptions in blueprints --- flask/testsuite/blueprints.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/flask/testsuite/blueprints.py b/flask/testsuite/blueprints.py index 72cf5182..57abdb6e 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__) From cff35237efea11c4bbc8c2fc75e24cbea30b26ce Mon Sep 17 00:00:00 2001 From: Michael Bikovitsky Date: Sat, 7 Sep 2013 18:21:15 +0300 Subject: [PATCH 0047/2502] Fix test_no_error_swallowing Path in assertion is now cross-platform. --- flask/testsuite/ext.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flask/testsuite/ext.py b/flask/testsuite/ext.py index 5cc3df43..7bdf6886 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(): From b7337080e48990393cf960890504710bc7cbc136 Mon Sep 17 00:00:00 2001 From: Michael Bikovitsky Date: Sat, 7 Sep 2013 18:29:49 +0300 Subject: [PATCH 0048/2502] Fix test_send_file_object static/index.html now opens in binary mode, and therefore newline conversion does not take place. --- flask/testsuite/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index 7de70c0a..4bb43ac2 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -260,7 +260,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: From 6ebe45b0ad6251fb8f38d9495f4f9354d508c2be Mon Sep 17 00:00:00 2001 From: Jet Sun Date: Sat, 7 Sep 2013 17:42:06 -0700 Subject: [PATCH 0049/2502] Fix typo. --- docs/patterns/jquery.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/jquery.rst b/docs/patterns/jquery.rst index 9de99f61..bb1b4c06 100644 --- a/docs/patterns/jquery.rst +++ b/docs/patterns/jquery.rst @@ -146,7 +146,7 @@ usually a better idea to have that in a separate script file: ?

calculate server side -I won't got into detail here about how jQuery works, just a very quick +I won't go into detail here about how jQuery works, just a very quick explanation of the little bit of code above: 1. ``$(function() { ... })`` specifies code that should run once the From bb882454a0c7b161a2ac29126a74b25dc8e12940 Mon Sep 17 00:00:00 2001 From: Oliver Beattie Date: Mon, 9 Sep 2013 15:13:42 +0100 Subject: [PATCH 0050/2502] Make the decorators attribute on View a tuple by default To discourage accidental runtime modification applying to all views. --- flask/views.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/views.py b/flask/views.py index b3b61b52..f4422ff4 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 From 8089eb576915e71e3238147631bb4a261b3366f6 Mon Sep 17 00:00:00 2001 From: Christopher Su Date: Thu, 19 Sep 2013 23:31:09 -0700 Subject: [PATCH 0051/2502] added missing import in sqlalchemy doc --- docs/patterns/sqlalchemy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index 07a762d8..fef9b2e9 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -177,7 +177,7 @@ SQL Abstraction Layer If you just want to use the database system (and SQL) abstraction layer you basically only need the engine:: - from sqlalchemy import create_engine, MetaData + from sqlalchemy import create_engine, MetaData, Table engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) metadata = MetaData(bind=engine) From 3d67736e090f55fcdc476452ac89a79fdd14cc23 Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Sat, 17 Aug 2013 22:40:06 +0000 Subject: [PATCH 0052/2502] Check error handlers for specific classes first This allows adding error handlers like this: @app.errorhandler(werkzeug.exceptions.Forbidden) And subclassing HTTPExceptions: class ForbiddenBecauseReason(Forbidden): pass @app.errorhandler(ForbiddenBecauseReason) def error1(): return "Forbidden because reason", 403 @app.errorhandler(403) def error2(): return "Forbidden", 403 ... the idea being, that a flask extension might want to raise an exception, with the default behaviour of creating a HTTP error page, but still allowing the user to add a view/handler specific to that exception (e.g., "Forbidden because you are not in the right group"). --- CHANGES | 6 ++++++ flask/app.py | 5 +++-- flask/testsuite/basic.py | 41 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 8699718c..dcedf67c 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,12 @@ Version 1.0 `False` it will only be modified if the session actually modifies. Non permanent sessions are not affected by this and will always expire if the browser window closes. +- Error handlers that match specific classes are now checked first, + thereby allowing catching exceptions that are subclasses of HTTP + exceptions (in ``werkzeug.execptions``). This makes it possible + for an extension author to create exceptions that will by default + result in the HTTP error of their choosing, but may be caught with + a custom error handler if desired. Version 0.10.2 -------------- diff --git a/flask/app.py b/flask/app.py index 805dc166..c97e8b3c 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1365,8 +1365,6 @@ class Flask(_PackageBoundObject): # wants the traceback preserved in handle_http_exception. Of course # we cannot prevent users from trashing it themselves in a custom # trap_http_exception method so that's their fault then. - if isinstance(e, HTTPException) and not self.trap_http_exception(e): - return self.handle_http_exception(e) blueprint_handlers = () handlers = self.error_handler_spec.get(request.blueprint) @@ -1377,6 +1375,9 @@ class Flask(_PackageBoundObject): if isinstance(e, typecheck): return handler(e) + if isinstance(e, HTTPException) and not self.trap_http_exception(e): + return self.handle_http_exception(e) + reraise(exc_type, exc_value, tb) def handle_exception(self, e): diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 71a1f832..8c13bda7 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -18,7 +18,7 @@ from datetime import datetime from threading import Thread from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning from flask._compat import text_type -from werkzeug.exceptions import BadRequest, NotFound +from werkzeug.exceptions import BadRequest, NotFound, Forbidden from werkzeug.http import parse_date from werkzeug.routing import BuildError @@ -626,12 +626,18 @@ class BasicFunctionalityTestCase(FlaskTestCase): @app.errorhandler(500) def internal_server_error(e): return 'internal server error', 500 + @app.errorhandler(Forbidden) + def forbidden(e): + return 'forbidden', 403 @app.route('/') def index(): flask.abort(404) @app.route('/error') def error(): 1 // 0 + @app.route('/forbidden') + def error2(): + flask.abort(403) c = app.test_client() rv = c.get('/') self.assert_equal(rv.status_code, 404) @@ -639,6 +645,9 @@ class BasicFunctionalityTestCase(FlaskTestCase): rv = c.get('/error') self.assert_equal(rv.status_code, 500) self.assert_equal(b'internal server error', rv.data) + rv = c.get('/forbidden') + self.assert_equal(rv.status_code, 403) + self.assert_equal(b'forbidden', rv.data) def test_before_request_and_routing_errors(self): app = flask.Flask(__name__) @@ -668,6 +677,36 @@ class BasicFunctionalityTestCase(FlaskTestCase): c = app.test_client() self.assert_equal(c.get('/').data, b'42') + def test_http_error_subclass_handling(self): + class ForbiddenSubclass(Forbidden): + pass + + app = flask.Flask(__name__) + @app.errorhandler(ForbiddenSubclass) + def handle_forbidden_subclass(e): + self.assert_true(isinstance(e, ForbiddenSubclass)) + return 'banana' + @app.errorhandler(403) + def handle_forbidden_subclass(e): + self.assert_false(isinstance(e, ForbiddenSubclass)) + self.assert_true(isinstance(e, Forbidden)) + return 'apple' + + @app.route('/1') + def index1(): + raise ForbiddenSubclass() + @app.route('/2') + def index2(): + flask.abort(403) + @app.route('/3') + def index3(): + raise Forbidden() + + c = app.test_client() + self.assert_equal(c.get('/1').data, b'banana') + self.assert_equal(c.get('/2').data, b'apple') + self.assert_equal(c.get('/3').data, b'apple') + def test_trapping_of_bad_request_key_errors(self): app = flask.Flask(__name__) app.testing = True From 231f45b432189500e760a63b41664f5f406793be Mon Sep 17 00:00:00 2001 From: dmackinnon Date: Wed, 25 Sep 2013 11:50:02 -0400 Subject: [PATCH 0053/2502] Fix typo --- docs/errorhandling.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst index 9e26196d..4db7a209 100644 --- a/docs/errorhandling.rst +++ b/docs/errorhandling.rst @@ -269,7 +269,7 @@ of the box (see :ref:`debug-mode`). If you would like to use another Python debugger, note that debuggers interfere with each other. You have to set some options in order to use your favorite debugger: -* ``debug`` - whether to enable debug mode and catch exceptinos +* ``debug`` - whether to enable debug mode and catch exceptions * ``use_debugger`` - whether to use the internal Flask debugger * ``use_reloader`` - whether to reload and fork the process on exception From 475b0c1cd94716efc1bb42ed0a2fd52dd7cf71c1 Mon Sep 17 00:00:00 2001 From: defuz Date: Thu, 26 Sep 2013 18:46:30 +0300 Subject: [PATCH 0054/2502] fix typo (jsonfiy) --- docs/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 1bc46afa..4cf7311a 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -155,7 +155,7 @@ The following configuration values are used internally by Flask: ascii-encoded JSON. If this is set to ``False`` Flask will not encode to ASCII and output strings as-is and return - unicode strings. ``jsonfiy`` will + unicode strings. ``jsonify`` will automatically encode it in ``utf-8`` then for transport for instance. ``JSON_SORT_KEYS`` By default Flask will serialize JSON From 3e485009a8ca606c742c88c92cfd178547b708cd Mon Sep 17 00:00:00 2001 From: defuz Date: Mon, 30 Sep 2013 18:40:35 +0300 Subject: [PATCH 0055/2502] add TEMPLATES_AUTO_RELOAD option to config --- CHANGES | 4 ++++ docs/config.rst | 9 +++++++++ flask/app.py | 7 +++++++ flask/testsuite/templating.py | 8 ++++++++ 4 files changed, 28 insertions(+) diff --git a/CHANGES b/CHANGES index 8699718c..acba98db 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,10 @@ Version 1.0 Non permanent sessions are not affected by this and will always expire if the browser window closes. +- 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/config.rst b/docs/config.rst index 1bc46afa..55e34786 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/flask/app.py b/flask/app.py index 1ea82fe7..066de664 100644 --- a/flask/app.py +++ b/flask/app.py @@ -294,6 +294,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 @@ -644,10 +645,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, diff --git a/flask/testsuite/templating.py b/flask/testsuite/templating.py index b2870dea..135612c1 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() From 5ecca4c0dae52c7645f9dbda6cbfc32ec76a3498 Mon Sep 17 00:00:00 2001 From: Hyunjun Kim Date: Wed, 2 Oct 2013 15:10:27 +0900 Subject: [PATCH 0056/2502] Fix a typo on blueprints module name. --- flask/blueprints.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/blueprints.py b/flask/blueprints.py index d45fd062..2c32502b 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -79,7 +79,7 @@ class BlueprintSetupState(object): class Blueprint(_PackageBoundObject): """Represents a blueprint. A blueprint is an object that records functions that will be called with the - :class:`~flask.blueprint.BlueprintSetupState` later to register functions + :class:`~flask.blueprints.BlueprintSetupState` later to register functions or other things on the main application. See :ref:`blueprints` for more information. From f161a71c190da7320d4a6be177c14928961fdf09 Mon Sep 17 00:00:00 2001 From: Alexey Shamrin Date: Thu, 3 Oct 2013 05:15:27 +0400 Subject: [PATCH 0057/2502] quickstart: import `request` in HTTP methods example --- docs/quickstart.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 3cb9b2f7..83ed2af8 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -257,6 +257,8 @@ accessing URLs. By default, a route only answers to `GET` requests, but that can be changed by providing the `methods` argument to the :meth:`~flask.Flask.route` decorator. Here are some examples:: + from flask import request + @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': From fc85bf42ae8fcd6cc76c734d0871bb78ad2a8f06 Mon Sep 17 00:00:00 2001 From: rsyring Date: Fri, 11 Oct 2013 08:39:46 -0400 Subject: [PATCH 0058/2502] Update appfactories.rst, make extension related section clearer --- docs/patterns/appfactories.rst | 38 +++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index 3ef80b42..5eeff541 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -54,20 +54,38 @@ get access to the application with the config? Use Here we look up the name of a template in the config. -Extension objects are not initially bound to an application. Using -``db.init_app``, the app gets configured for the extension. No -application-specific state is stored on the extension object, so one extension -object can be used for multiple apps. For more information about the design of -extensions refer to :doc:`/extensiondev`. +Factories & Extensions +---------------------- -Your `model.py` might look like this when using `Flask-SQLAlchemy -`_:: +It's preferable to create your extensions and app factories so that the +extension object does not initially get bound to the application. + +Using `Flask-SQLAlchemy `_, +as an example, you should **not** do:: + + def create_app(config_filename): + app = Flask(__name__) + app.config.from_pyfile(config_filename) + + db = SQLAlchemy(app) + +But, rather, in model.py (or equivalent):: - from flask.ext.sqlalchemy import SQLAlchemy - # no app object passed! Instead we use use db.init_app in the factory. db = SQLAlchemy() + +and in your application.py (or equivalent):: - # create some models + def create_app(config_filename): + app = Flask(__name__) + app.config.from_pyfile(config_filename) + + from yourapplication.model import db + db.init_app(app) + + +Using this design pattern, no application-specific state is stored on the +extension object, so one extension object can be used for multiple apps. +For more information about the design of extensions refer to :doc:`/extensiondev`. Using Applications ------------------ From e31a2a80ec4254e8ac8127558bc29cfd23d05511 Mon Sep 17 00:00:00 2001 From: John Hobbs Date: Fri, 11 Oct 2013 17:03:54 -0500 Subject: [PATCH 0059/2502] Update url_for documentation Previous documentation referenced a non-existent property on the Flask object called "build_error_handler". This should actually reference Flask.url_build_error_handlers. --- flask/helpers.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index e59f7d3c..2d8f6d11 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. From b7f21831fa71fa74f1b37ee23f2b7744f886a61f Mon Sep 17 00:00:00 2001 From: talam Date: Sat, 12 Oct 2013 18:14:26 -0400 Subject: [PATCH 0060/2502] Updated some typos Fixed some incorrect variable references. --- docs/tutorial/dbcon.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst index 55526fbf..fb5a0c4a 100644 --- a/docs/tutorial/dbcon.rst +++ b/docs/tutorial/dbcon.rst @@ -4,7 +4,7 @@ Step 3: Database Connections ---------------------------- We have created a function for establishing a database connection with -`create_db` but by itself that's not particularly useful. Creating and +`connect_db` but by itself that's not particularly useful. Creating and closing database connections all the time is very inefficient, so we want to keep it around for longer. Because database connections encapsulate a transaction we also need to make sure that only one request at the time @@ -22,7 +22,7 @@ variable associated with the current application context. We will go into the details of this a bit later. For the time being all you have to know is that you can store information -savely on the :data:`~flask.g` object. +safely on the :data:`~flask.g` object. So when do you put it on there? To do that you can make a helper function. The first time the function is called it will create a database @@ -67,7 +67,7 @@ Continue to :ref:`tutorial-dbinit`. where to put the code from this step and the next. A logical place is to group these module-level functions together, and put your new ``get_db`` and ``close_db`` functions below your existing - ``init_db`` function (following the tutorial line-by-line). + ``connect_db`` function (following the tutorial line-by-line). If you need a moment to find your bearings, take a look at how the `example source`_ is organized. In Flask, you can put all of your application code From 1df0f2dc1cc227ba005d402dacfd6ee44b97e637 Mon Sep 17 00:00:00 2001 From: Matt Skone Date: Tue, 15 Oct 2013 09:17:19 -0700 Subject: [PATCH 0061/2502] Escaped 'text' keyword in column name. --- examples/flaskr/schema.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ); From a7f5d60789bbc360147b85af04e4412865122ccf Mon Sep 17 00:00:00 2001 From: Sean Cronin Date: Tue, 15 Oct 2013 12:37:25 -0400 Subject: [PATCH 0062/2502] Makes the error names consistent --- docs/patterns/apierrors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/apierrors.rst b/docs/patterns/apierrors.rst index 264b9ae5..b06966e6 100644 --- a/docs/patterns/apierrors.rst +++ b/docs/patterns/apierrors.rst @@ -47,7 +47,7 @@ At that point views can raise that error, but it would immediately result in an internal server error. The reason for this is that there is no handler registered for this error class. That however is easy to add:: - @app.errorhandler(InvalidAPIUsage) + @app.errorhandler(InvalidUsage) def handle_invalid_usage(error): response = jsonify(error.to_dict()) response.status_code = error.status_code From 46b5754d97d11327efac22618ad5994851656937 Mon Sep 17 00:00:00 2001 From: Adrian Date: Tue, 15 Oct 2013 21:47:42 +0200 Subject: [PATCH 0063/2502] Don't refer to flaskext in docs --- flask/app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/app.py b/flask/app.py index 1ea82fe7..167ea32d 100644 --- a/flask/app.py +++ b/flask/app.py @@ -470,8 +470,8 @@ class Flask(_PackageBoundObject): #: app.extensions = {} #: app.extensions['extensionname'] = SomeObject() #: - #: The key must match the name of the `flaskext` module. For example in - #: case of a "Flask-Foo" extension in `flaskext.foo`, the key would be + #: The key must match the name of the extension module. For example in + #: case of a "Flask-Foo" extension in `flask_foo`, the key would be #: ``'foo'``. #: #: .. versionadded:: 0.7 From a3a2f521f14ba90689efc661afe7a0375409c83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Wed, 16 Oct 2013 20:12:20 +0200 Subject: [PATCH 0064/2502] Clear exceptions when pushing a context Fixes #882 --- CHANGES | 2 ++ flask/ctx.py | 5 +++++ flask/testsuite/appctx.py | 17 +++++++++++++++++ flask/testsuite/reqctx.py | 16 ++++++++++++++++ 4 files changed, 40 insertions(+) diff --git a/CHANGES b/CHANGES index 9e23bcfb..1a5d697e 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,8 @@ Version 0.10.2 - Raise an :exc:`AttributeError` in :func:`flask.helpers.find_package` with a useful message explaining why it is raised when a PEP 302 import hook is used without an `is_package()` method. +- Fixed an issue causing exceptions raised before entering a request or app + context to be passed to teardown handlers. Version 0.10.1 -------------- diff --git a/flask/ctx.py b/flask/ctx.py index f1342378..363e7f6e 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -163,6 +163,8 @@ class AppContext(object): def push(self): """Binds the app context to the current context.""" self._refcnt += 1 + if hasattr(sys, 'exc_clear'): + sys.exc_clear() _app_ctx_stack.push(self) appcontext_pushed.send(self.app) @@ -312,6 +314,9 @@ class RequestContext(object): else: self._implicit_app_ctx_stack.append(None) + if hasattr(sys, 'exc_clear'): + sys.exc_clear() + _request_ctx_stack.push(self) # Open the session at the moment that the request context is diff --git a/flask/testsuite/appctx.py b/flask/testsuite/appctx.py index 8524d22b..6c3d0595 100644 --- a/flask/testsuite/appctx.py +++ b/flask/testsuite/appctx.py @@ -63,6 +63,23 @@ class AppContextTestCase(FlaskTestCase): self.assert_equal(cleanup_stuff, [None]) + def test_app_tearing_down_with_previous_exception(self): + cleanup_stuff = [] + app = flask.Flask(__name__) + @app.teardown_appcontext + def cleanup(exception): + cleanup_stuff.append(exception) + + try: + raise Exception('dummy') + except Exception: + pass + + with app.app_context(): + pass + + self.assert_equal(cleanup_stuff, [None]) + def test_custom_app_ctx_globals_class(self): class CustomRequestGlobals(object): def __init__(self): diff --git a/flask/testsuite/reqctx.py b/flask/testsuite/reqctx.py index c232a74c..38016b5e 100644 --- a/flask/testsuite/reqctx.py +++ b/flask/testsuite/reqctx.py @@ -33,6 +33,22 @@ class RequestContextTestCase(FlaskTestCase): ctx.pop() self.assert_equal(buffer, [None]) + def test_teardown_with_previous_exception(self): + buffer = [] + app = flask.Flask(__name__) + @app.teardown_request + def end_of_request(exception): + buffer.append(exception) + + try: + raise Exception('dummy') + except Exception: + pass + + with app.test_request_context(): + self.assert_equal(buffer, []) + self.assert_equal(buffer, [None]) + def test_proper_test_request_context(self): app = flask.Flask(__name__) app.config.update( From 5689ef36398c8f5a85bfe2335875560f6614f3b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Tue, 22 Oct 2013 14:01:33 +0200 Subject: [PATCH 0065/2502] Fix typo s/at at/at a/ --- docs/reqcontext.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reqcontext.rst b/docs/reqcontext.rst index 4da5acd8..d6088e92 100644 --- a/docs/reqcontext.rst +++ b/docs/reqcontext.rst @@ -137,7 +137,7 @@ Teardown Callbacks ------------------ The teardown callbacks are special callbacks in that they are executed at -at different point. Strictly speaking they are independent of the actual +a different point. Strictly speaking they are independent of the actual request handling as they are bound to the lifecycle of the :class:`~flask.ctx.RequestContext` object. When the request context is popped, the :meth:`~flask.Flask.teardown_request` functions are called. From 9cecf0d81c58e5d0727d83e4ee11352997546354 Mon Sep 17 00:00:00 2001 From: Christopher Su Date: Tue, 22 Oct 2013 11:37:35 -0700 Subject: [PATCH 0066/2502] moved Table import to next code block --- docs/patterns/sqlalchemy.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index fef9b2e9..3c4d9ce9 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -177,7 +177,7 @@ SQL Abstraction Layer If you just want to use the database system (and SQL) abstraction layer you basically only need the engine:: - from sqlalchemy import create_engine, MetaData, Table + from sqlalchemy import create_engine, MetaData engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) metadata = MetaData(bind=engine) @@ -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 From c021e58775eaf8e2dc7a91c903dc1c95dc18e829 Mon Sep 17 00:00:00 2001 From: Day Barr Date: Fri, 25 Oct 2013 16:51:29 +0100 Subject: [PATCH 0067/2502] Fix typo in docs for error_handler_spec --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index 167ea32d..42a1943d 100644 --- a/flask/app.py +++ b/flask/app.py @@ -361,7 +361,7 @@ class Flask(_PackageBoundObject): #: A dictionary of all registered error handlers. The key is `None` #: for error handlers active on the application, otherwise the key is #: the name of the blueprint. Each key points to another dictionary - #: where they key is the status code of the http exception. The + #: where the key is the status code of the http exception. The #: special key `None` points to a list of tuples where the first item #: is the class for the instance check and the second the error handler #: function. From 05dbf52fa586ccd86a7d774e918faa5e9ae414a5 Mon Sep 17 00:00:00 2001 From: d3spis3d Date: Wed, 30 Oct 2013 11:29:18 +1100 Subject: [PATCH 0068/2502] URL Reversing in Quickstart #779 Updated quickstart to make clear that URL reversing refers to the use of url_for() to build the URL. Issue #779 --- docs/quickstart.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 3cb9b2f7..a10149ae 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -236,8 +236,9 @@ below. It tells Flask to behave as though it is handling a request, even though we are interacting with it through a Python shell. Have a look at the explanation below. :ref:`context-locals`). -Why would you want to build URLs instead of hard-coding them into your -templates? There are three good reasons for this: +Why would you want to build URLs using the URL reversing function :func:`~flask.url_for` +instead of hard-coding them into your templates? There are three good reasons +for this: 1. Reversing is often more descriptive than hard-coding the URLs. More importantly, it allows you to change URLs in one go, without having to From ecb31c680451cbcbf3b933e620d3bf4ee42a8042 Mon Sep 17 00:00:00 2001 From: Pengfei Xue Date: Wed, 30 Oct 2013 16:45:44 +0800 Subject: [PATCH 0069/2502] fix typo --- docs/appcontext.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/appcontext.rst b/docs/appcontext.rst index 40a075e4..3d2412f0 100644 --- a/docs/appcontext.rst +++ b/docs/appcontext.rst @@ -82,7 +82,7 @@ moves between threads and it will not be shared between requests. As such it is the perfect place to store database connection information and other things. The internal stack object is called :data:`flask._app_ctx_stack`. Extensions are free to store additional information on the topmost level, -assuming they pick a sufficiently unique name and should put there +assuming they pick a sufficiently unique name and should put their information there, instead on the :data:`flask.g` object which is reserved for user code. From 0d648fa4685ee18dfc4a4fe056d366d76e67ab58 Mon Sep 17 00:00:00 2001 From: Reetta Vaahtoranta Date: Sat, 9 Nov 2013 13:13:14 +0000 Subject: [PATCH 0070/2502] Changed the wording of some sentences there were difficult to understand. --- docs/tutorial/dbinit.rst | 10 ++++------ docs/tutorial/folders.rst | 4 ++-- docs/tutorial/schema.rst | 6 ++---- docs/tutorial/setup.rst | 13 +++++-------- 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 8f64c3b5..418fe638 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -3,8 +3,8 @@ Step 4: Creating The Database ============================= -Flaskr is a database powered application as outlined earlier, and more -precisely, an application powered by a relational database system. Such +As outlined earlier, Flaskr is a database powered application, and more +precisely, it is an application powered by a relational database system. Such systems need a schema that tells them how to store that information. So before starting the server for the first time it's important to create that schema. @@ -15,13 +15,11 @@ Such a schema can be created by piping the `schema.sql` file into the sqlite3 /tmp/flaskr.db < schema.sql The downside of this is that it requires the sqlite3 command to be -installed which is not necessarily the case on every system. Also one has -to provide the path to the database there which leaves some place for -errors. It's a good idea to add a function that initializes the database +installed which is not necessarily the case on every system. This also require that we provide the path to the database which can introduce errors. It's a good idea to add a function that initializes the database for you to the application. To do this we can create a function called `init_db` that initializes the -database. Let me show you the code first. Just add that function below +database. Let me show you the code first. Just add this function below the `connect_db` function in `flaskr.py`:: def init_db(): diff --git a/docs/tutorial/folders.rst b/docs/tutorial/folders.rst index 61080932..4bf47cd7 100644 --- a/docs/tutorial/folders.rst +++ b/docs/tutorial/folders.rst @@ -11,8 +11,8 @@ application:: /templates The `flaskr` folder is not a python package, but just something where we -drop our files. Directly into this folder we will then put our database -schema as well as main module in the following steps. The files inside +drop our files. We will then put our database schema as well as main module +into this folder. It is done in the following way. The files inside the `static` folder are available to users of the application via `HTTP`. This is the place where css and javascript files go. Inside the `templates` folder Flask will look for `Jinja2`_ templates. The diff --git a/docs/tutorial/schema.rst b/docs/tutorial/schema.rst index e4aa2f59..93b431aa 100644 --- a/docs/tutorial/schema.rst +++ b/docs/tutorial/schema.rst @@ -3,10 +3,8 @@ Step 1: Database Schema ======================= -First we want to create the database schema. For this application only a -single table is needed and we only want to support SQLite so that is quite -easy. Just put the following contents into a file named `schema.sql` in -the just created `flaskr` folder: +First we want to create the database schema. Only a single table is needed +for this application and we only want to support SQLite so creating the database schema is quite easy. Just put the following contents into a file named `schema.sql` in the just created `flaskr` folder: .. sourcecode:: sql diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 5b059c56..ff3b92a8 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -4,11 +4,10 @@ Step 2: Application Setup Code ============================== Now that we have the schema in place we can create the application module. -Let's call it `flaskr.py` inside the `flaskr` folder. For starters we -will add the imports and create the application object. For small -applications it's a possibility to drop the configuration directly into -the module which we will be doing here. However a cleaner solution would -be to create a separate `.ini` or `.py` file and load that or import the +Let's call it flaskr.py. We will place this file inside the flask folder. +We will begin by adding the imports we need and by adding the config section. +For small applications, it is possible to drop the configuration directly into +the module, and this is what we will be doing here. However a cleaner solution would be to create a separate `.ini` or `.py` file and load that or import the values from there. First we add the imports in `flaskr.py`:: @@ -66,9 +65,7 @@ debug flag enables or disables the interactive debugger. *Never leave debug mode activated in a production system*, because it will allow users to execute code on the server! -We also add a method to easily connect to the database specified. That -can be used to open a connection on request and also from the interactive -Python shell or a script. This will come in handy later. We create a +We will also add a method that allows for easily connecting to the specified database. This can be used to open a connection on request and also from the interactive Python shell or a script. This will come in handy later. We create a simple database connection through SQLite and then tell it to use the :class:`sqlite3.Row` object to represent rows. This allows us to treat the rows as if they were dictionaries instead of tuples. From afd3c4532b8625729bed9ed37a3eddd0b7b3b5a9 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 9 Nov 2013 13:41:09 +0000 Subject: [PATCH 0071/2502] Rewrapped lines --- docs/tutorial/dbinit.rst | 4 +++- docs/tutorial/schema.rst | 6 ++++-- docs/tutorial/setup.rst | 22 +++++++++++++--------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 418fe638..10c77a27 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -15,7 +15,9 @@ Such a schema can be created by piping the `schema.sql` file into the sqlite3 /tmp/flaskr.db < schema.sql The downside of this is that it requires the sqlite3 command to be -installed which is not necessarily the case on every system. This also require that we provide the path to the database which can introduce errors. It's a good idea to add a function that initializes the database +installed which is not necessarily the case on every system. This also +require that we provide the path to the database which can introduce +errors. It's a good idea to add a function that initializes the database for you to the application. To do this we can create a function called `init_db` that initializes the diff --git a/docs/tutorial/schema.rst b/docs/tutorial/schema.rst index 93b431aa..f8455037 100644 --- a/docs/tutorial/schema.rst +++ b/docs/tutorial/schema.rst @@ -3,8 +3,10 @@ Step 1: Database Schema ======================= -First we want to create the database schema. Only a single table is needed -for this application and we only want to support SQLite so creating the database schema is quite easy. Just put the following contents into a file named `schema.sql` in the just created `flaskr` folder: +First we want to create the database schema. Only a single table is needed +for this application and we only want to support SQLite so creating the +database schema is quite easy. Just put the following contents into a file +named `schema.sql` in the just created `flaskr` folder: .. sourcecode:: sql diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index ff3b92a8..acf227f9 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -4,11 +4,12 @@ Step 2: Application Setup Code ============================== Now that we have the schema in place we can create the application module. -Let's call it flaskr.py. We will place this file inside the flask folder. -We will begin by adding the imports we need and by adding the config section. -For small applications, it is possible to drop the configuration directly into -the module, and this is what we will be doing here. However a cleaner solution would be to create a separate `.ini` or `.py` file and load that or import the -values from there. +Let's call it flaskr.py. We will place this file inside the flask folder. +We will begin by adding the imports we need and by adding the config +section. For small applications, it is possible to drop the configuration +directly into the module, and this is what we will be doing here. However +a cleaner solution would be to create a separate `.ini` or `.py` file and +load that or import the values from there. First we add the imports in `flaskr.py`:: @@ -65,10 +66,13 @@ debug flag enables or disables the interactive debugger. *Never leave debug mode activated in a production system*, because it will allow users to execute code on the server! -We will also add a method that allows for easily connecting to the specified database. This can be used to open a connection on request and also from the interactive Python shell or a script. This will come in handy later. We create a -simple database connection through SQLite and then tell it to use the -:class:`sqlite3.Row` object to represent rows. This allows us to treat -the rows as if they were dictionaries instead of tuples. +We will also add a method that allows for easily connecting to the +specified database. This can be used to open a connection on request and +also from the interactive Python shell or a script. This will come in +handy later. We create a simple database connection through SQLite and +then tell it to use the :class:`sqlite3.Row` object to represent rows. +This allows us to treat the rows as if they were dictionaries instead of +tuples. :: From 67c165bcbb4028bd8335342f230692100fcd9776 Mon Sep 17 00:00:00 2001 From: Afik Date: Mon, 16 Dec 2013 22:04:51 -0800 Subject: [PATCH 0072/2502] Update celery.rst With the latest version of Celery (3.1.6), following this tutorial produces the following error when attempting to start the celery worker: user_preload = tuple(self.app.user_options['preload'] or ()) AttributeError: 'Flask' object has no attribute 'user_options' Using `app` as the variable name here confuses celery. Renaming `app` to `flask_app` in the tutorial solves the issue and allows the celery worker to start successfully. --- docs/patterns/celery.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/patterns/celery.rst b/docs/patterns/celery.rst index c7cd3922..60d32418 100644 --- a/docs/patterns/celery.rst +++ b/docs/patterns/celery.rst @@ -60,12 +60,12 @@ Flask:: from flask import Flask - app = Flask(__name__) - app.config.update( + flask_app = Flask(__name__) + flask_app.config.update( CELERY_BROKER_URL='redis://localhost:6379', CELERY_RESULT_BACKEND='redis://localhost:6379' ) - celery = make_celery(app) + celery = make_celery(flask_app) @celery.task() From 6a37c1716dcd0431878dad230f9111c268d2ad98 Mon Sep 17 00:00:00 2001 From: Mark Hildreth Date: Tue, 24 Dec 2013 14:36:28 -0500 Subject: [PATCH 0073/2502] Fixed incorrect grammar in Config.rst Fixed incorrect grammar in "SESSION_REFRESH_EACH_REQUEST" configuration description. --- docs/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 1bc46afa..9cde9d24 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -89,7 +89,7 @@ The following configuration values are used internally by Flask: Starting with Flask 0.8 this can also be an integer representing seconds. ``SESSION_REFRESH_EACH_REQUEST`` this flag controls how permanent - sessions are refresh. If set to `True` + sessions are refreshed. If set to `True` (which is the default) then the cookie is refreshed each request which automatically bumps the lifetime. If From 5addabfbddbb0f620d81adc7bcafbf8bd5f12a3e Mon Sep 17 00:00:00 2001 From: Zachary Wright Heller Date: Tue, 24 Dec 2013 20:12:34 -0800 Subject: [PATCH 0074/2502] Fix typo in helpers.py --- flask/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/helpers.py b/flask/helpers.py index e59f7d3c..bc09bf3f 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -359,7 +359,7 @@ def flash(message, category='message'): # session.setdefault('_flashes', []).append((category, message)) # # This assumed that changes made to mutable structures in the session are - # are always in sync with the sess on object, which is not true for session + # are always in sync with the session object, which is not true for session # implementations that use external storage for keeping their keys/values. flashes = session.get('_flashes', []) flashes.append((category, message)) From 70f8b39c52527573e65e7066024a187867564c01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85hl=C3=A9n?= Date: Tue, 31 Dec 2013 22:16:13 +0100 Subject: [PATCH 0075/2502] added a new behaviour for responses that enable the tuple to be in the form of (response, headers) and continiue to support the (response, status, headers) format. --- docs/quickstart.rst | 6 +++--- flask/app.py | 23 ++++++++++++++--------- flask/testsuite/basic.py | 22 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 12 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 3cb9b2f7..c8a1140b 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -676,9 +676,9 @@ converting return values into response objects is as follows: default parameters. 3. If a tuple is returned the items in the tuple can provide extra information. Such tuples have to be in the form ``(response, status, - headers)`` where at least one item has to be in the tuple. The - `status` value will override the status code and `headers` can be a - list or dictionary of additional header values. + headers)`` or ``(response, headers)`` where at least one item has + to be in the tuple. The `status` value will override the status code + and `headers` can be a list or dictionary of additional header values. 4. If none of that works, Flask will assume the return value is a valid WSGI application and convert that into a response object. diff --git a/flask/app.py b/flask/app.py index 42a1943d..ea31393f 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1544,7 +1544,8 @@ class Flask(_PackageBoundObject): a WSGI function the function is called as WSGI application and buffered as response object :class:`tuple` A tuple in the form ``(response, status, - headers)`` where `response` is any of the + headers)`` or ``(response, headers)`` + where `response` is any of the types defined here, `status` is a string or an integer and `headers` is a list or a dictionary with header values. @@ -1556,34 +1557,38 @@ class Flask(_PackageBoundObject): Previously a tuple was interpreted as the arguments for the response object. """ - status = headers = None + status_or_headers = headers = None if isinstance(rv, tuple): - rv, status, headers = rv + (None,) * (3 - len(rv)) + rv, status_or_headers, headers = rv + (None,) * (3 - len(rv)) if rv is None: raise ValueError('View function did not return a response') + if isinstance(status_or_headers, (dict, list)): + headers, status_or_headers = status_or_headers, None + if not isinstance(rv, self.response_class): # When we create a response object directly, we let the constructor # set the headers and status. We do this because there can be # some extra logic involved when creating these objects with # specific values (like default content type selection). if isinstance(rv, (text_type, bytes, bytearray)): - rv = self.response_class(rv, headers=headers, status=status) - headers = status = None + rv = self.response_class(rv, headers=headers, status=status_or_headers) + headers = status_or_headers = None else: rv = self.response_class.force_type(rv, request.environ) - if status is not None: - if isinstance(status, string_types): - rv.status = status + if status_or_headers is not None: + if isinstance(status_or_headers, string_types): + rv.status = status_or_headers else: - rv.status_code = status + rv.status_code = status_or_headers if headers: rv.headers.extend(headers) return rv + def create_url_adapter(self, request): """Creates a URL adapter for the given request. The URL adapter is created at a point where the request context is not yet set up diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 71a1f832..be11fa6b 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -735,7 +735,17 @@ class BasicFunctionalityTestCase(FlaskTestCase): return 'Meh', 400, { 'X-Foo': 'Testing', 'Content-Type': 'text/plain; charset=utf-8' + } + @app.route("/two_args") + def from_two_args_tuple(): + return "Hello", { + 'X-Foo': 'Test', + 'Content-Type': 'text/plain; charset=utf-8' } + @app.route("/args_status") + def from_status_tuple(): + return "Hi, status!", 400 + c = app.test_client() self.assert_equal(c.get('/unicode').data, u'Hällo Wörld'.encode('utf-8')) self.assert_equal(c.get('/string').data, u'Hällo Wörld'.encode('utf-8')) @@ -745,6 +755,18 @@ class BasicFunctionalityTestCase(FlaskTestCase): self.assert_equal(rv.status_code, 400) self.assert_equal(rv.mimetype, 'text/plain') + rv2 = c.get("/two_args") + self.assert_equal(rv2.data, b'Hello') + self.assert_equal(rv2.headers['X-Foo'], 'Test') + self.assert_equal(rv2.status_code, 200) + self.assert_equal(rv2.mimetype, 'text/plain') + + rv3 = c.get("/args_status") + self.assert_equal(rv3.data, b'Hi, status!') + self.assert_equal(rv3.status_code, 400) + self.assert_equal(rv3.mimetype, 'text/html') + + def test_make_response(self): app = flask.Flask(__name__) with app.test_request_context(): From dbc40961913381ecf1513fa2467aefeef6d4d41b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikael=20=C3=85hl=C3=A9n?= Date: Tue, 31 Dec 2013 22:59:00 +0100 Subject: [PATCH 0076/2502] added a new test case with a Response instance and cleaned up the code --- flask/testsuite/basic.py | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index be11fa6b..902a23f3 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -736,15 +736,21 @@ class BasicFunctionalityTestCase(FlaskTestCase): 'X-Foo': 'Testing', 'Content-Type': 'text/plain; charset=utf-8' } - @app.route("/two_args") + @app.route('/two_args') def from_two_args_tuple(): - return "Hello", { + return 'Hello', { 'X-Foo': 'Test', 'Content-Type': 'text/plain; charset=utf-8' } - @app.route("/args_status") + @app.route('/args_status') def from_status_tuple(): - return "Hi, status!", 400 + return 'Hi, status!', 400 + @app.route('/args_header') + def from_response_instance_status_tuple(): + return flask.Response('Hello world', 404), { + "X-Foo": "Bar", + "X-Bar": "Foo" + } c = app.test_client() self.assert_equal(c.get('/unicode').data, u'Hällo Wörld'.encode('utf-8')) @@ -754,18 +760,20 @@ class BasicFunctionalityTestCase(FlaskTestCase): self.assert_equal(rv.headers['X-Foo'], 'Testing') self.assert_equal(rv.status_code, 400) self.assert_equal(rv.mimetype, 'text/plain') - - rv2 = c.get("/two_args") + rv2 = c.get('/two_args') self.assert_equal(rv2.data, b'Hello') self.assert_equal(rv2.headers['X-Foo'], 'Test') self.assert_equal(rv2.status_code, 200) self.assert_equal(rv2.mimetype, 'text/plain') - - rv3 = c.get("/args_status") + rv3 = c.get('/args_status') self.assert_equal(rv3.data, b'Hi, status!') self.assert_equal(rv3.status_code, 400) self.assert_equal(rv3.mimetype, 'text/html') - + rv4 = c.get('/args_header') + self.assert_equal(rv4.data, b'Hello world') + self.assert_equal(rv4.headers['X-Foo'], 'Bar') + self.assert_equal(rv4.headers['X-Bar'], 'Foo') + self.assert_equal(rv4.status_code, 404) def test_make_response(self): app = flask.Flask(__name__) From 52098e1e4fc4d9c962e9ecb09dfbbcc6ac80a4d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Thu, 2 Jan 2014 19:21:07 +0100 Subject: [PATCH 0077/2502] Happy New Year 2014 --- examples/flaskr/flaskr.py | 2 +- examples/flaskr/flaskr_tests.py | 2 +- examples/jqueryexample/jqueryexample.py | 2 +- examples/minitwit/minitwit.py | 2 +- examples/minitwit/minitwit_tests.py | 2 +- flask/__init__.py | 2 +- flask/_compat.py | 2 +- flask/app.py | 2 +- flask/blueprints.py | 2 +- flask/config.py | 2 +- flask/ctx.py | 2 +- flask/debughelpers.py | 2 +- flask/ext/__init__.py | 2 +- flask/exthook.py | 2 +- flask/globals.py | 2 +- flask/helpers.py | 2 +- flask/json.py | 2 +- flask/logging.py | 2 +- flask/module.py | 2 +- flask/sessions.py | 2 +- flask/signals.py | 2 +- flask/templating.py | 2 +- flask/testing.py | 2 +- flask/testsuite/__init__.py | 2 +- flask/testsuite/appctx.py | 2 +- flask/testsuite/basic.py | 2 +- flask/testsuite/blueprints.py | 2 +- flask/testsuite/config.py | 2 +- flask/testsuite/deprecations.py | 2 +- flask/testsuite/examples.py | 2 +- flask/testsuite/ext.py | 2 +- flask/testsuite/helpers.py | 2 +- flask/testsuite/regression.py | 2 +- flask/testsuite/reqctx.py | 2 +- flask/testsuite/signals.py | 2 +- flask/testsuite/subclassing.py | 2 +- flask/testsuite/templating.py | 2 +- flask/testsuite/testing.py | 2 +- flask/testsuite/views.py | 2 +- flask/views.py | 2 +- flask/wrappers.py | 2 +- scripts/flask-07-upgrade.py | 2 +- scripts/flaskext_compat.py | 2 +- scripts/flaskext_test.py | 2 +- scripts/make-release.py | 2 +- 45 files changed, 45 insertions(+), 45 deletions(-) diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index b193e94e..faf1f543 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -6,7 +6,7 @@ A microblog example application written as Flask tutorial with Flask and sqlite3. - :copyright: (c) 2010 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/examples/flaskr/flaskr_tests.py b/examples/flaskr/flaskr_tests.py index dd16c038..ab08a60a 100644 --- a/examples/flaskr/flaskr_tests.py +++ b/examples/flaskr/flaskr_tests.py @@ -5,7 +5,7 @@ Tests the Flaskr application. - :copyright: (c) 2010 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import os diff --git a/examples/jqueryexample/jqueryexample.py b/examples/jqueryexample/jqueryexample.py index 0e8caf3e..08164119 100644 --- a/examples/jqueryexample/jqueryexample.py +++ b/examples/jqueryexample/jqueryexample.py @@ -5,7 +5,7 @@ A simple application that shows how Flask and jQuery get along. - :copyright: (c) 2010 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from flask import Flask, jsonify, render_template, request diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py index baa204f9..913d4522 100644 --- a/examples/minitwit/minitwit.py +++ b/examples/minitwit/minitwit.py @@ -5,7 +5,7 @@ A microblogging application written with Flask and sqlite3. - :copyright: (c) 2010 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/examples/minitwit/minitwit_tests.py b/examples/minitwit/minitwit_tests.py index c213466d..9b027629 100644 --- a/examples/minitwit/minitwit_tests.py +++ b/examples/minitwit/minitwit_tests.py @@ -5,7 +5,7 @@ Tests the MiniTwit application. - :copyright: (c) 2010 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import os diff --git a/flask/__init__.py b/flask/__init__.py index c60f82ba..2ab3356c 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -6,7 +6,7 @@ A microframework based on Werkzeug. It's extensively documented and follows best practice patterns. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/_compat.py b/flask/_compat.py index c3428845..e69dac45 100644 --- a/flask/_compat.py +++ b/flask/_compat.py @@ -7,7 +7,7 @@ version of six so we don't have to depend on a specific version of it. - :copyright: (c) 2013 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import sys diff --git a/flask/app.py b/flask/app.py index addc40b4..a4bcd66e 100644 --- a/flask/app.py +++ b/flask/app.py @@ -5,7 +5,7 @@ This module implements the central WSGI application object. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/blueprints.py b/flask/blueprints.py index 4575ec9b..ef536ddc 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -6,7 +6,7 @@ Blueprints are the recommended way to implement larger or more pluggable applications in Flask 0.7 and later. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from functools import update_wrapper diff --git a/flask/config.py b/flask/config.py index 155afa2f..07d6fbc8 100644 --- a/flask/config.py +++ b/flask/config.py @@ -5,7 +5,7 @@ Implements the configuration related objects. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/ctx.py b/flask/ctx.py index 363e7f6e..7f99dfb4 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -5,7 +5,7 @@ Implements the objects required to keep the context. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/debughelpers.py b/flask/debughelpers.py index 2f8510f9..a96c13ea 100644 --- a/flask/debughelpers.py +++ b/flask/debughelpers.py @@ -5,7 +5,7 @@ Various helpers to make the development experience better. - :copyright: (c) 2011 by Armin Ronacher. +1 :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from ._compat import implements_to_string diff --git a/flask/ext/__init__.py b/flask/ext/__init__.py index f29958a1..315d7e5e 100644 --- a/flask/ext/__init__.py +++ b/flask/ext/__init__.py @@ -14,7 +14,7 @@ We're switching from namespace packages because it was just too painful for everybody involved. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/exthook.py b/flask/exthook.py index d0d814c6..0422817f 100644 --- a/flask/exthook.py +++ b/flask/exthook.py @@ -16,7 +16,7 @@ This is used by `flask.ext`. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import sys diff --git a/flask/globals.py b/flask/globals.py index 67d41f5c..fecbfc10 100644 --- a/flask/globals.py +++ b/flask/globals.py @@ -6,7 +6,7 @@ Defines all the global objects that are proxies to the current active context. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/helpers.py b/flask/helpers.py index e59f7d3c..c71da85b 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -5,7 +5,7 @@ Implements various helpers. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/json.py b/flask/json.py index 45ba3240..49de2320 100644 --- a/flask/json.py +++ b/flask/json.py @@ -5,7 +5,7 @@ Implementation helpers for the JSON support in Flask. - :copyright: (c) 2012 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import io diff --git a/flask/logging.py b/flask/logging.py index 9ad641d1..5022a696 100644 --- a/flask/logging.py +++ b/flask/logging.py @@ -5,7 +5,7 @@ Implements the logging support for Flask. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/module.py b/flask/module.py index 1c4f466c..336d687b 100644 --- a/flask/module.py +++ b/flask/module.py @@ -5,7 +5,7 @@ Implements a class that represents module blueprints. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/sessions.py b/flask/sessions.py index 3246eb83..abb477a1 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -5,7 +5,7 @@ Implements cookie based sessions based on itsdangerous. - :copyright: (c) 2012 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/signals.py b/flask/signals.py index 7bd0385a..ac71b8ce 100644 --- a/flask/signals.py +++ b/flask/signals.py @@ -6,7 +6,7 @@ Implements signals based on blinker if available, otherwise falls silently back to a noop - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ signals_available = False diff --git a/flask/templating.py b/flask/templating.py index 63adb092..c34af652 100644 --- a/flask/templating.py +++ b/flask/templating.py @@ -5,7 +5,7 @@ Implements the bridge to Jinja2. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import posixpath diff --git a/flask/testing.py b/flask/testing.py index 1dc383af..8f28f77b 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -6,7 +6,7 @@ Implements test support helpers. This module is lazily imported and usually not used in production environments. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/__init__.py b/flask/testsuite/__init__.py index 7fe61484..695243c6 100644 --- a/flask/testsuite/__init__.py +++ b/flask/testsuite/__init__.py @@ -6,7 +6,7 @@ Tests Flask itself. The majority of Flask is already tested as part of Werkzeug. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/appctx.py b/flask/testsuite/appctx.py index 6c3d0595..55f7dcba 100644 --- a/flask/testsuite/appctx.py +++ b/flask/testsuite/appctx.py @@ -5,7 +5,7 @@ Tests the application context. - :copyright: (c) 2012 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 51fd46f2..1858ca5c 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -5,7 +5,7 @@ The basic functionality. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/blueprints.py b/flask/testsuite/blueprints.py index 72cf5182..8a0e5a79 100644 --- a/flask/testsuite/blueprints.py +++ b/flask/testsuite/blueprints.py @@ -5,7 +5,7 @@ Blueprints (and currently modules) - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py index 13e14d85..cdc6273f 100644 --- a/flask/testsuite/config.py +++ b/flask/testsuite/config.py @@ -5,7 +5,7 @@ Configuration and instances. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/deprecations.py b/flask/testsuite/deprecations.py index 56371822..2d77925e 100644 --- a/flask/testsuite/deprecations.py +++ b/flask/testsuite/deprecations.py @@ -5,7 +5,7 @@ Tests deprecation support. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/examples.py b/flask/testsuite/examples.py index 2d30958f..1ea3e6cc 100644 --- a/flask/testsuite/examples.py +++ b/flask/testsuite/examples.py @@ -5,7 +5,7 @@ Tests the examples. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import os diff --git a/flask/testsuite/ext.py b/flask/testsuite/ext.py index 5cc3df43..3c75540e 100644 --- a/flask/testsuite/ext.py +++ b/flask/testsuite/ext.py @@ -5,7 +5,7 @@ Tests the extension import thing. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index 636f67fa..518d9384 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -5,7 +5,7 @@ Various helpers. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/regression.py b/flask/testsuite/regression.py index e516dc0f..ad06aa59 100644 --- a/flask/testsuite/regression.py +++ b/flask/testsuite/regression.py @@ -5,7 +5,7 @@ Tests regressions. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/reqctx.py b/flask/testsuite/reqctx.py index 38016b5e..c8954824 100644 --- a/flask/testsuite/reqctx.py +++ b/flask/testsuite/reqctx.py @@ -5,7 +5,7 @@ Tests the request context. - :copyright: (c) 2012 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/signals.py b/flask/testsuite/signals.py index 45ca45d9..93f1c7c8 100644 --- a/flask/testsuite/signals.py +++ b/flask/testsuite/signals.py @@ -5,7 +5,7 @@ Signalling. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/subclassing.py b/flask/testsuite/subclassing.py index 6b81db98..24c823ae 100644 --- a/flask/testsuite/subclassing.py +++ b/flask/testsuite/subclassing.py @@ -6,7 +6,7 @@ Test that certain behavior of flask can be customized by subclasses. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import flask diff --git a/flask/testsuite/templating.py b/flask/testsuite/templating.py index b2870dea..0f0ad5b4 100644 --- a/flask/testsuite/templating.py +++ b/flask/testsuite/templating.py @@ -5,7 +5,7 @@ Template functionality - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/testing.py b/flask/testsuite/testing.py index a618f0b8..3aa9e688 100644 --- a/flask/testsuite/testing.py +++ b/flask/testsuite/testing.py @@ -5,7 +5,7 @@ Test client and more. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/testsuite/views.py b/flask/testsuite/views.py index 4eee015b..bd572b24 100644 --- a/flask/testsuite/views.py +++ b/flask/testsuite/views.py @@ -5,7 +5,7 @@ Pluggable views. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/flask/views.py b/flask/views.py index b3b61b52..607cb7a4 100644 --- a/flask/views.py +++ b/flask/views.py @@ -5,7 +5,7 @@ This module provides class-based views inspired by the ones in Django. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from .globals import request diff --git a/flask/wrappers.py b/flask/wrappers.py index 1a17824a..e77b9c20 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -5,7 +5,7 @@ Implements the WSGI wrappers (request and response). - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ diff --git a/scripts/flask-07-upgrade.py b/scripts/flask-07-upgrade.py index e1017e69..18395b50 100644 --- a/scripts/flask-07-upgrade.py +++ b/scripts/flask-07-upgrade.py @@ -16,7 +16,7 @@ the most common patterns at least. The diff it generates should be hand reviewed and not applied blindly without making backups. - :copyright: (c) Copyright 2011 by Armin Ronacher. + :copyright: (c) Copyright 2014 by Armin Ronacher. :license: see LICENSE for more details. """ import re diff --git a/scripts/flaskext_compat.py b/scripts/flaskext_compat.py index 050fd7e0..357e88ad 100644 --- a/scripts/flaskext_compat.py +++ b/scripts/flaskext_compat.py @@ -12,7 +12,7 @@ flaskext_compat.activate() from flask.ext import foo - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import sys diff --git a/scripts/flaskext_test.py b/scripts/flaskext_test.py index 5b0d9d22..bbffc3a2 100644 --- a/scripts/flaskext_test.py +++ b/scripts/flaskext_test.py @@ -5,7 +5,7 @@ Tests the Flask extensions. - :copyright: (c) 2010 by Ali Afshar. + :copyright: (c) 2014 by Ali Afshar. :license: BSD, see LICENSE for more details. """ diff --git a/scripts/make-release.py b/scripts/make-release.py index 31f4fa9e..d6375820 100644 --- a/scripts/make-release.py +++ b/scripts/make-release.py @@ -7,7 +7,7 @@ Helper script that performs a release. Does pretty much everything automatically for us. - :copyright: (c) 2011 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ import sys From 85aa4ec8c15937121d1eb1e73f0b5d029d2f1ee1 Mon Sep 17 00:00:00 2001 From: bool-dev Date: Sat, 4 Jan 2014 17:21:18 +0530 Subject: [PATCH 0078/2502] Updated greenlet links, which were dead. --- docs/deploying/wsgi-standalone.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/deploying/wsgi-standalone.rst b/docs/deploying/wsgi-standalone.rst index 7a3ef0a9..c8d4f20e 100644 --- a/docs/deploying/wsgi-standalone.rst +++ b/docs/deploying/wsgi-standalone.rst @@ -25,7 +25,7 @@ For example, to run a Flask application with 4 worker processes (``-w .. _Gunicorn: http://gunicorn.org/ .. _eventlet: http://eventlet.net/ -.. _greenlet: http://codespeak.net/py/0.9.2/greenlet.html +.. _greenlet: http://greenlet.readthedocs.org/en/latest/ Tornado -------- @@ -63,7 +63,7 @@ event loop:: http_server.serve_forever() .. _Gevent: http://www.gevent.org/ -.. _greenlet: http://codespeak.net/py/0.9.2/greenlet.html +.. _greenlet: http://greenlet.readthedocs.org/en/latest/ .. _libevent: http://monkey.org/~provos/libevent/ Twisted Web From cf0953619e2fa3a79e93b6195ca3b39f25552c3d Mon Sep 17 00:00:00 2001 From: skeuomorf Date: Mon, 6 Jan 2014 17:58:54 +0200 Subject: [PATCH 0079/2502] Added a favicon for the docs --- docs/_static/flask-favicon.ico | Bin 0 -> 2238 bytes docs/conf.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 docs/_static/flask-favicon.ico diff --git a/docs/_static/flask-favicon.ico b/docs/_static/flask-favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..bf0a961573dcfb1eddccf97c1caefd03b807633c GIT binary patch literal 2238 zcmeIyYb>5;7{Kwr)@$n2SsltS?=Yu1P1&$v&J5d}iU8> z%KWYjI)H_I$wPR`*Yo&ULqS0SMMXs<5(!F5N+>HUqoSgMs;Vk#YHFyftD~Wzfu^P= zT3T9YYipyYr-#12J_ZH`7#bR4WMqU?D#gUa1XEK}%*@O%H#f(^!U9W6ORTJ{u(r0w z#>NI)TU+ex?69}D$HBn?CnqPIot<%UalzHq6*o6G+}+*r@bJLX(-SW*FTB0I@%8n^ z&(9Bke}4i30tgHYBq%6|;NW0FLP7`)4J9lrjPUSqA|fJ)jEp2IDvIdnXkubwh>eXU zE-sGv_;?Z$5=cx;Bq=G0_dtgNJ}s*39BYHDg~sI9G~ zuC5N5Oh$cuJq-;FG&MER+}un{OAD>7t+chZ(ca!pM@I*pot<=bb-3=R%5G&IEU@Gv7IBaDuYGB!5Gg9i_I`0yd)~g@pwc7Z+JtT4H&5nU$3lR##V9TU%p&eVvVs z4IV#!%;x4MTU%RfZ*Q}+v%~K0E_-`>JbChjr%#`;wD~3cZXiFGpO>3jONF3zvnWcutvQdBExOT0!u7?*%=7sSeiVHO(s) zLX}I*DfsK7@e6~u-1K#H?^L~hM}&ol%ebmI77I5&YY^i+cWk3B(Nv#NDaQ4#s0xa{ zt-?dL&?oiho7bWPeY}P03XznLFJSp)O3UrLMHdyzMLbJE%{bs& zOFf&EToILrWEeUIX}(b^u7Xgfgw)`NmY1>6dR;9|ytRLqgt))7_D`t!?QH)y`U Date: Sat, 11 Jan 2014 01:09:30 +0100 Subject: [PATCH 0080/2502] Use json method Use json method of response.Response --- examples/persona/persona.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/persona/persona.py b/examples/persona/persona.py index ff785a4f..a6e12808 100644 --- a/examples/persona/persona.py +++ b/examples/persona/persona.py @@ -38,7 +38,7 @@ def login_handler(): 'audience': request.host_url, }, verify=True) if resp.ok: - verification_data = json.loads(resp.content) + verification_data = resp.json() if verification_data['status'] == 'okay': session['email'] = verification_data['email'] return 'OK' From b81b040110d18d6aa3633c1132420bf29be00c6b Mon Sep 17 00:00:00 2001 From: Tommaso Allevi Date: Sat, 11 Jan 2014 01:13:32 +0100 Subject: [PATCH 0081/2502] Remove unused import --- examples/persona/persona.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/persona/persona.py b/examples/persona/persona.py index a6e12808..d56f299a 100644 --- a/examples/persona/persona.py +++ b/examples/persona/persona.py @@ -1,4 +1,4 @@ -from flask import Flask, render_template, session, request, json, abort, g +from flask import Flask, render_template, session, request, abort, g import requests From b0bb6c7f3d2e80731428fcc50ab1edd45b58959d Mon Sep 17 00:00:00 2001 From: Matt Iversen Date: Sat, 11 Jan 2014 11:31:22 +1100 Subject: [PATCH 0082/2502] Release as universal python wheel (2/3 compat) --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index 6116ecd1..fc7b9c95 100644 --- a/setup.cfg +++ b/setup.cfg @@ -3,3 +3,6 @@ tag_date = true [aliases] release = egg_info -RDb '' + +[wheel] +universal = 1 From de3dbd3125263f93a1b900f65091f6f0163544b2 Mon Sep 17 00:00:00 2001 From: Spittie Date: Sun, 12 Jan 2014 02:54:08 +0100 Subject: [PATCH 0083/2502] Update fileuploads.rst secure_filename is under werkzeug.utils (http://werkzeug.pocoo.org/docs/utils/) --- docs/patterns/fileuploads.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index d237b107..eef50a9a 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -22,7 +22,7 @@ bootstrapping code for our application:: import os from flask import Flask, request, redirect, url_for - from werkzeug import secure_filename + from werkzeug.utils import secure_filename UPLOAD_FOLDER = '/path/to/the/uploads' ALLOWED_EXTENSIONS = set(['txt', 'pdf', 'png', 'jpg', 'jpeg', 'gif']) From cb61696b47e48a84d42391e58c1bc2f926d4018f Mon Sep 17 00:00:00 2001 From: "Ifiok Jr." Date: Mon, 20 Jan 2014 04:28:40 +0000 Subject: [PATCH 0084/2502] Update appcontext.rst small edit --- docs/appcontext.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/appcontext.rst b/docs/appcontext.rst index 3d2412f0..a9d302b7 100644 --- a/docs/appcontext.rst +++ b/docs/appcontext.rst @@ -83,7 +83,7 @@ it is the perfect place to store database connection information and other things. The internal stack object is called :data:`flask._app_ctx_stack`. Extensions are free to store additional information on the topmost level, assuming they pick a sufficiently unique name and should put their -information there, instead on the :data:`flask.g` object which is reserved +information there, instead of on the :data:`flask.g` object which is reserved for user code. For more information about that, see :ref:`extension-dev`. From 90a50f8b51bdd71d9f2b1fd82352628cfd681898 Mon Sep 17 00:00:00 2001 From: Matt Wright Date: Thu, 23 Jan 2014 15:05:37 -0500 Subject: [PATCH 0085/2502] Add `get_namespace` method on `Config` object for convenient access of namespaced config options. --- flask/config.py | 34 ++++++++++++++++++++++++++++++++++ flask/testsuite/config.py | 15 +++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/flask/config.py b/flask/config.py index 07d6fbc8..66bf7eb4 100644 --- a/flask/config.py +++ b/flask/config.py @@ -164,5 +164,39 @@ class Config(dict): if key.isupper(): self[key] = getattr(obj, key) + def get_namespace(self, namespace, lowercase=True): + """Returns a dictionary containing a subset of configuration options + that match the specified namespace/prefix. Example usage:: + + app.config['IMAGE_STORE_TYPE'] = 'fs' + app.config['IMAGE_STORE_PATH'] = '/var/app/images' + app.config['IMAGE_STORE_BASE_URL'] = 'http://img.website.com' + image_store_config = app.config.get_namespace('IMAGE_STORE_') + + The resulting dictionary `image_store` would look like:: + + { + 'type': 'fs', + 'path': '/var/app/images', + 'base_url': 'http://img.website.com' + } + + This is often useful when configuration options map directly to + keyword arguments in functions or class constructors. + + :param namespace: a configuration namespace + :param lowercase: a flag indicating if the keys of the resulting + dictionary should be lowercase + """ + rv = {} + for k, v in self.iteritems(): + if not k.startswith(namespace): + continue + key = k[len(namespace):] + if lowercase: + key = key.lower() + rv[key] = v + return rv + def __repr__(self): return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self)) diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py index cdc6273f..c2c14cb1 100644 --- a/flask/testsuite/config.py +++ b/flask/testsuite/config.py @@ -105,6 +105,21 @@ class ConfigTestCase(FlaskTestCase): app.config['PERMANENT_SESSION_LIFETIME'] = 42 self.assert_equal(app.permanent_session_lifetime.seconds, 42) + def test_get_namespace(self): + app = flask.Flask(__name__) + app.config['FOO_OPTION_1'] = 'foo option 1' + app.config['FOO_OPTION_2'] = 'foo option 2' + app.config['BAR_STUFF_1'] = 'bar stuff 1' + app.config['BAR_STUFF_2'] = 'bar stuff 2' + foo_options = app.config.get_namespace('FOO_') + self.assert_equal(2, len(foo_options)) + self.assert_equal('foo option 1', foo_options['option_1']) + self.assert_equal('foo option 2', foo_options['option_2']) + bar_options = app.config.get_namespace('BAR_', lowercase=False) + self.assert_equal(2, len(bar_options)) + self.assert_equal('bar stuff 1', bar_options['STUFF_1']) + self.assert_equal('bar stuff 2', bar_options['STUFF_2']) + class LimitedLoaderMockWrapper(object): def __init__(self, loader): From 21aa620b6bd65e8368f86a37e55de8e56fccf917 Mon Sep 17 00:00:00 2001 From: Guriec Corbel Date: Fri, 24 Jan 2014 12:43:23 -0500 Subject: [PATCH 0086/2502] Remove the extra "1" --- flask/debughelpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/debughelpers.py b/flask/debughelpers.py index db875775..b1159dba 100644 --- a/flask/debughelpers.py +++ b/flask/debughelpers.py @@ -5,7 +5,7 @@ Various helpers to make the development experience better. -1 :copyright: (c) 2014 by Armin Ronacher. + :copyright: (c) 2014 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ from ._compat import implements_to_string From ea656a89099ee848c42c871a8407242d853c0a09 Mon Sep 17 00:00:00 2001 From: Nicholas Zaccardi Date: Fri, 24 Jan 2014 19:21:24 -0600 Subject: [PATCH 0087/2502] added a note to the api documentation about JSON_SORT_KEYS. Fixes #922 --- docs/api.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index 5faccc30..db550500 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -385,6 +385,12 @@ you are using Flask 0.10 which implies that: doSomethingWith({{ user.username|tojson|safe }}); +.. admonition:: Auto-Sort JSON Keys + + The configuration variable ``JSON_SORT_KEYS`` (:ref:`config`) can be set to false to + stop Flask from auto-sorting keys. By default sorting is enabled and + outside of the app context sorting is turned on. + .. autofunction:: jsonify .. autofunction:: dumps From 6ec83e18dca497a8fbfca6caca5999984bd32f2e Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 26 Jan 2014 17:34:37 +0000 Subject: [PATCH 0088/2502] Added a workaround for a pypy bug in context managers --- flask/_compat.py | 24 ++++++++++++++++++++++++ flask/ctx.py | 7 +++++++ 2 files changed, 31 insertions(+) diff --git a/flask/_compat.py b/flask/_compat.py index e69dac45..86a87832 100644 --- a/flask/_compat.py +++ b/flask/_compat.py @@ -71,3 +71,27 @@ def with_metaclass(meta, *bases): return type.__new__(cls, name, (), d) return meta(name, bases, d) return metaclass('temporary_class', None, {}) + + +# Certain versions of pypy have a bug where clearing the exception stack +# breaks the __exit__ function in a very peculiar way. This is currently +# true for pypy 2.2.1 for instance. The second level of exception blocks +# is necessary because pypy seems to forget to check if an exception +# happend until the next bytecode instruction? +BROKEN_PYPY_CTXMGR_EXIT = False +if hasattr(sys, 'pypy_version_info'): + class _Mgr(object): + def __enter__(self): + return self + def __exit__(self, *args): + sys.exc_clear() + try: + try: + with _Mgr(): + raise AssertionError() + except: + raise + except TypeError: + BROKEN_PYPY_CTXMGR_EXIT = True + except AssertionError: + pass diff --git a/flask/ctx.py b/flask/ctx.py index 7f99dfb4..406d80c5 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -19,6 +19,7 @@ from werkzeug.exceptions import HTTPException from .globals import _request_ctx_stack, _app_ctx_stack from .module import blueprint_is_module from .signals import appcontext_pushed, appcontext_popped +from ._compat import BROKEN_PYPY_CTXMGR_EXIT class _AppCtxGlobals(object): @@ -187,6 +188,9 @@ class AppContext(object): def __exit__(self, exc_type, exc_value, tb): self.pop(exc_value) + if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: + raise exc_type, exc_value, tb + class RequestContext(object): """The request context contains all request relevant information. It is @@ -390,6 +394,9 @@ class RequestContext(object): # See flask.testing for how this works. self.auto_pop(exc_value) + if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: + raise exc_type, exc_value, tb + def __repr__(self): return '<%s \'%s\' [%s] of %s>' % ( self.__class__.__name__, From 52e1c383faf44b88407f3b9a36293c8ab98b78a3 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 26 Jan 2014 17:47:16 +0000 Subject: [PATCH 0089/2502] Make Python 3 happy --- flask/ctx.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/ctx.py b/flask/ctx.py index 406d80c5..b9c933e9 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -189,7 +189,7 @@ class AppContext(object): self.pop(exc_value) if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: - raise exc_type, exc_value, tb + reraise(exc_type, exc_value, tb) class RequestContext(object): @@ -395,7 +395,7 @@ class RequestContext(object): self.auto_pop(exc_value) if BROKEN_PYPY_CTXMGR_EXIT and exc_type is not None: - raise exc_type, exc_value, tb + reraise(exc_type, exc_value, tb) def __repr__(self): return '<%s \'%s\' [%s] of %s>' % ( From e78961c812f0a81792bb9e1eae26fd96a10069c0 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 26 Jan 2014 18:08:02 +0000 Subject: [PATCH 0090/2502] Fixed a missing import --- flask/ctx.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/ctx.py b/flask/ctx.py index b9c933e9..e2b906cb 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -19,7 +19,7 @@ from werkzeug.exceptions import HTTPException from .globals import _request_ctx_stack, _app_ctx_stack from .module import blueprint_is_module from .signals import appcontext_pushed, appcontext_popped -from ._compat import BROKEN_PYPY_CTXMGR_EXIT +from ._compat import BROKEN_PYPY_CTXMGR_EXIT, reraise class _AppCtxGlobals(object): From 3d6de4dd7980d5a9bd8f031d01830486c4d1607c Mon Sep 17 00:00:00 2001 From: Alex Pearce Date: Wed, 5 Feb 2014 20:11:06 +0000 Subject: [PATCH 0091/2502] Explicitly pass the Celery instance name. When starting a Celery worker module, it is assumed that the Celery instance within the module is called `app`. This is not the case in the Celery pattern, where it is called `celery`, and so must be explicitly referenced. --- docs/patterns/celery.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/celery.rst b/docs/patterns/celery.rst index c7cd3922..9bbb38ff 100644 --- a/docs/patterns/celery.rst +++ b/docs/patterns/celery.rst @@ -86,7 +86,7 @@ disappointed to learn that your ``.wait()`` will never actually return. That's because you also need to run celery. You can do that by running celery as a worker:: - $ celery -A your_application worker + $ celery -A your_application.celery worker The ``your_application`` string has to point to your application's package or module that creates the `celery` object. From 3dab2f3d419c9b0c506b9029191bd0ebd1a7768e Mon Sep 17 00:00:00 2001 From: Martin Polden Date: Fri, 7 Feb 2014 10:26:26 +0100 Subject: [PATCH 0092/2502] Update copyright year --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 3c590329..9319a8f3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2013 by Armin Ronacher and contributors. See AUTHORS +Copyright (c) 2014 by Armin Ronacher and contributors. See AUTHORS for more details. Some rights reserved. From f5a02b94952e38d35e476acf2bc8b6e0ba2f89fd Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 16:27:26 +0000 Subject: [PATCH 0093/2502] Added a warning about key sorting --- docs/api.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index db550500..c87055d1 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -387,9 +387,12 @@ you are using Flask 0.10 which implies that: .. admonition:: Auto-Sort JSON Keys - The configuration variable ``JSON_SORT_KEYS`` (:ref:`config`) can be set to false to - stop Flask from auto-sorting keys. By default sorting is enabled and - outside of the app context sorting is turned on. + The configuration variable ``JSON_SORT_KEYS`` (:ref:`config`) can be + set to false to stop Flask from auto-sorting keys. By default sorting + is enabled and outside of the app context sorting is turned on. + + Notice that disabling key sorting can cause issues when using content + based HTTP caches and Python's hash randomization feature. .. autofunction:: jsonify From e5bba9deb5c9ab9c66bea5c17e96741777fe46ab Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 17:01:13 +0000 Subject: [PATCH 0094/2502] Added support for custom JSON mimetypes --- CHANGES | 1 + flask/testsuite/helpers.py | 9 +++++++++ flask/wrappers.py | 17 ++++++++++++++++- 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 99d00134..ed5ad9a2 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,7 @@ Version 1.0 `False` it will only be modified if the session actually modifies. Non permanent sessions are not affected by this and will always expire if the browser window closes. +- Made Flask support custom JSON mimetypes for incoming data. Version 0.10.2 -------------- diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index f8edd4e0..f8460d3c 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -38,6 +38,15 @@ class JSONTestCase(FlaskTestCase): rv = c.post('/json', data='malformed', content_type='application/json') self.assert_equal(rv.status_code, 400) + def test_json_custom_mimetypes(self): + app = flask.Flask(__name__) + @app.route('/json', methods=['POST']) + def return_json(): + return flask.request.get_json() + c = app.test_client() + rv = c.post('/json', data='"foo"', content_type='application/x+json') + self.assert_equal(rv.data, b'foo') + def test_json_body_encoding(self): app = flask.Flask(__name__) app.testing = True diff --git a/flask/wrappers.py b/flask/wrappers.py index 038ba6fc..1d6fbef5 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -107,6 +107,21 @@ class Request(RequestBase): # XXX: deprecate property return self.get_json() + @property + def is_json(self): + """Indicates if this request is JSON or not. By default a request + is considered to include JSON data if the mimetype is + ``application/json`` or ``application/*+json``. + + .. versionadded:: 0.11 + """ + mt = self.mimetype + if mt == 'application/json': + return True + if mt.startswith('application/') and mt.endswith('+json'): + return True + return False + def get_json(self, force=False, silent=False, cache=True): """Parses the incoming JSON request data and returns it. If parsing fails the :meth:`on_json_loading_failed` method on the @@ -124,7 +139,7 @@ class Request(RequestBase): if rv is not _missing: return rv - if self.mimetype != 'application/json' and not force: + if not (force or self.is_json): return None # We accept a request charset against the specification as From e7c587789ae22a626196cea26340253dd9b70bf1 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 17:19:00 +0000 Subject: [PATCH 0095/2502] Fixe a bug in the test client causing url parameters to be removed. This fixes #968 --- CHANGES | 2 ++ flask/testing.py | 2 ++ flask/testsuite/testing.py | 14 ++++++++++++++ 3 files changed, 18 insertions(+) diff --git a/CHANGES b/CHANGES index 1a5d697e..656b9356 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,8 @@ Version 0.10.2 without an `is_package()` method. - Fixed an issue causing exceptions raised before entering a request or app context to be passed to teardown handlers. +- Fixed an issue with query parameters getting removed from requests in + the test client when absolute URLs were requested. Version 0.10.1 -------------- diff --git a/flask/testing.py b/flask/testing.py index 8f28f77b..6351462b 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -31,6 +31,8 @@ def make_test_environ_builder(app, path='/', base_url=None, *args, **kwargs): base_url += app_root.lstrip('/') if url.netloc: path = url.path + if url.query: + path += '?' + url.query return EnvironBuilder(path, base_url, *args, **kwargs) diff --git a/flask/testsuite/testing.py b/flask/testsuite/testing.py index 3aa9e688..609f3a94 100644 --- a/flask/testsuite/testing.py +++ b/flask/testsuite/testing.py @@ -196,6 +196,20 @@ class TestToolsTestCase(FlaskTestCase): self.assert_equal(called, [None]) self.assert_equal(called, [None, None]) + def test_full_url_request(self): + app = flask.Flask(__name__) + app.testing = True + + @app.route('/action', methods=['POST']) + def action(): + return 'x' + + with app.test_client() as c: + rv = c.post('http://domain.com/action?vodka=42', data={'gin': 43}) + self.assert_equal(rv.status_code, 200) + self.assert_('gin' in flask.request.form) + self.ssert_('vodka' in flask.request.args) + class SubdomainTestCase(FlaskTestCase): From 6de9484c97a9965ba2373015d28ab79280b85abe Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 17:32:54 +0000 Subject: [PATCH 0096/2502] Fixed a typo --- flask/testsuite/testing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/testsuite/testing.py b/flask/testsuite/testing.py index 609f3a94..de78746b 100644 --- a/flask/testsuite/testing.py +++ b/flask/testsuite/testing.py @@ -208,7 +208,7 @@ class TestToolsTestCase(FlaskTestCase): rv = c.post('http://domain.com/action?vodka=42', data={'gin': 43}) self.assert_equal(rv.status_code, 200) self.assert_('gin' in flask.request.form) - self.ssert_('vodka' in flask.request.args) + self.assert_('vodka' in flask.request.args) class SubdomainTestCase(FlaskTestCase): From 6075797f13f58fb89d3d07f50885e41c8a211ec1 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 17:33:42 +0000 Subject: [PATCH 0097/2502] Added changelog entry --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index a18dcd0e..2559bc04 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,8 @@ Version 1.0 Non permanent sessions are not affected by this and will always expire if the browser window closes. - Made Flask support custom JSON mimetypes for incoming data. +- Added support for returning tuples in the form ``(response, headers)`` + from a view function. Version 0.10.2 -------------- From ae45aed8d6802fbea8f3693d82b6e89c4e51429d Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 17:36:12 +0000 Subject: [PATCH 0098/2502] Removed an unnecessary newline --- flask/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index a4bcd66e..8827fcdb 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1159,7 +1159,6 @@ class Flask(_PackageBoundObject): """ self.jinja_env.tests[name or f.__name__] = f - @setupmethod def template_global(self, name=None): """A decorator that is used to register a custom template global function. From 46c24da0162208ddc82ea755347967e4162b97c3 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 17:39:26 +0000 Subject: [PATCH 0099/2502] Make before_first_request a decorator --- CHANGES | 1 + flask/app.py | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 656b9356..0737c590 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,7 @@ Version 0.10.2 context to be passed to teardown handlers. - Fixed an issue with query parameters getting removed from requests in the test client when absolute URLs were requested. +- Made `@before_first_request` into a decorator as intended. Version 0.10.1 -------------- diff --git a/flask/app.py b/flask/app.py index 8827fcdb..c3170df6 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1205,6 +1205,7 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.8 """ self.before_first_request_funcs.append(f) + return f @setupmethod def after_request(self, f): From 2506c0b9a96d48f14624acfa552fc88e38a542ae Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 17:45:09 +0000 Subject: [PATCH 0100/2502] Fixed sending etags for file streams with a name. This fixes #930. --- CHANGES | 1 + flask/helpers.py | 21 +++++++++++++-------- flask/testsuite/helpers.py | 11 +++++++++++ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 0737c590..8b76c5fc 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Version 0.10.2 - Fixed an issue with query parameters getting removed from requests in the test client when absolute URLs were requested. - Made `@before_first_request` into a decorator as intended. +- Fixed an etags bug when sending a file without a name. Version 0.10.1 -------------- diff --git a/flask/helpers.py b/flask/helpers.py index c71da85b..7b69b63c 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -538,14 +538,19 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, rv.expires = int(time() + cache_timeout) if add_etags and filename is not None: - rv.set_etag('flask-%s-%s-%s' % ( - os.path.getmtime(filename), - os.path.getsize(filename), - adler32( - filename.encode('utf-8') if isinstance(filename, text_type) - else filename - ) & 0xffffffff - )) + try: + rv.set_etag('flask-%s-%s-%s' % ( + os.path.getmtime(filename), + os.path.getsize(filename), + adler32( + filename.encode('utf-8') if isinstance(filename, text_type) + else filename + ) & 0xffffffff + )) + except OSError: + warn('Access %s failed, maybe it does not exist, so ignore etags in ' + 'headers' % filename, stacklevel=2) + if conditional: rv = rv.make_conditional(request) # make sure we don't send x-sendfile for servers that diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index 518d9384..c645dc14 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -264,6 +264,17 @@ class SendfileTestCase(FlaskTestCase): rv.close() # etags self.assert_equal(len(captured), 1) + with catch_warnings() as captured: + from StringIO import StringIO as PYStringIO + f = PYStringIO('Test') + f.name = 'test.txt' + rv = flask.send_file(f) + rv.direct_passthrough = False + self.assert_equal(rv.data, b'Test') + self.assert_equal(rv.mimetype, 'text/plain') + rv.close() + # attachment_filename and etags + self.assert_equal(len(captured), 3) with catch_warnings() as captured: f = StringIO('Test') rv = flask.send_file(f, mimetype='text/plain') From e67d9a04b2f5ccd16ed6bd62b1d5ca1526bbf0ab Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 17:46:02 +0000 Subject: [PATCH 0101/2502] Fixed a changelog entry --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 8b76c5fc..b66432af 100644 --- a/CHANGES +++ b/CHANGES @@ -17,7 +17,7 @@ Version 0.10.2 - Fixed an issue with query parameters getting removed from requests in the test client when absolute URLs were requested. - Made `@before_first_request` into a decorator as intended. -- Fixed an etags bug when sending a file without a name. +- Fixed an etags bug when sending a file streams with a name. Version 0.10.1 -------------- From 9074bc46af69f168e059dc977f90bbeb6d250077 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 22:07:13 +0000 Subject: [PATCH 0102/2502] Switch away from /tmp for windows users --- docs/tutorial/setup.rst | 19 ++++++++++++++----- examples/flaskr/flaskr.py | 3 ++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index acf227f9..ed8a744c 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -27,7 +27,7 @@ config from the same file, in `flaskr.py`:: # Load default config and override config from an environment variable app.config.update(dict( - DATABASE='/tmp/flaskr.db', + DATABASE=os.path.join(app.root_path, 'flaskr.db'), DEBUG=True, SECRET_KEY='development key', USERNAME='admin', @@ -38,11 +38,20 @@ config from the same file, in `flaskr.py`:: The :class:`~flask.Config` object works similar to a dictionary so we can update it with new values. -.. admonition:: Windows +.. admonition:: Database Path - If you are on Windows, replace `/tmp/flaskr.db` with a different writeable - path of your choice, in the configuration and for the rest of this - tutorial. + Operating systems know the concept of a current working directory for + each process. Unfortunately you cannot depend on this in web + applications because you might have more than one application in the + same process. + + For this reason the ``app.root_path`` attribute can be used to + get the path to the application. Together with the ``os.path`` module + files can then easily be found. In this example we place the + database right next to it. + + For a real-work application it's recommended to use + :ref:`instance-folders` instead. Usually, it is a good idea to load a separate, environment specific configuration file. Flask allows you to import multiple configurations and it diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index 65622c01..384162af 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -10,6 +10,7 @@ :license: BSD, see LICENSE for more details. """ +import os from sqlite3 import dbapi2 as sqlite3 from flask import Flask, request, session, g, redirect, url_for, abort, \ render_template, flash @@ -20,7 +21,7 @@ app = Flask(__name__) # Load default config and override config from an environment variable app.config.update(dict( - DATABASE='/tmp/flaskr.db', + DATABASE=os.path.join(app.root_path, 'flaskr.db'), DEBUG=True, SECRET_KEY='development key', USERNAME='admin', From a8a98bc6b61bfc3842667bc97f977fd250bf7403 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 22:10:08 +0000 Subject: [PATCH 0103/2502] Various documentation fixups --- docs/deploying/fastcgi.rst | 25 +++++++++++++------------ docs/signals.rst | 1 + docs/tutorial/dbinit.rst | 2 +- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst index 430a9748..b4f01d57 100644 --- a/docs/deploying/fastcgi.rst +++ b/docs/deploying/fastcgi.rst @@ -54,14 +54,14 @@ can execute it: Configuring Apache ------------------ -The example above is good enough for a basic Apache deployment but your `.fcgi` -file will appear in your application URL e.g. -example.com/yourapplication.fcgi/news/. There are few ways to configure your -application so that yourapplication.fcgi does not appear in the URL. A -preferable way is to use the ScriptAlias and SetHandler configuration directives -to route requests to the FastCGI server. The following example uses -FastCgiServer to start 5 instances of the application which will handle all -incoming requests: +The example above is good enough for a basic Apache deployment but your +`.fcgi` file will appear in your application URL e.g. +example.com/yourapplication.fcgi/news/. There are few ways to configure +your application so that yourapplication.fcgi does not appear in the URL. +A preferable way is to use the ScriptAlias and SetHandler configuration +directives to route requests to the FastCGI server. The following example +uses FastCgiServer to start 5 instances of the application which will +handle all incoming requests:: LoadModule fastcgi_module /usr/lib64/httpd/modules/mod_fastcgi.so @@ -79,10 +79,11 @@ incoming requests: -These processes will be managed by Apache. If you're using an standalone FastCGI -server, you can use the FastCgiExternalServer directive instead. Note that in -the following the path is not real, it's simply used as an identifier to other -directives such as AliasMatch: +These processes will be managed by Apache. If you're using an standalone +FastCGI server, you can use the FastCgiExternalServer directive instead. +Note that in the following the path is not real, it's simply used as an +identifier to other +directives such as AliasMatch:: FastCgiServer /var/www/html/yourapplication -host 127.0.0.1:3000 diff --git a/docs/signals.rst b/docs/signals.rst index 5d574965..c9df1edf 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -322,6 +322,7 @@ The following signals exist in Flask: .. versionadded:: 0.10 .. data:: flask.appcontext_popped + :noindex: This signal is sent when an application context is popped. The sender is the application. This usually falls in line with the diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 10c77a27..980caca0 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -38,7 +38,7 @@ hand. Without an application context the :data:`~flask.g` object does not know yet to which application it becomes as there could be more than one! The ``with app.app_context()`` statement establishes the application -context for us. In the body of the with statement the :flask:`~flask.g` +context for us. In the body of the with statement the :data:`~flask.g` object will be associated with ``app``. At the end of the with statement the association is released and all teardown functions are executed. This means that our database connection is disconnected after the commit. From 6f44ca705f277580105d8e55a7a3994802ac7e7d Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 22:14:23 +0000 Subject: [PATCH 0104/2502] Fixed sqlite3 pattern. This fixes #924 --- docs/patterns/sqlite3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/sqlite3.rst b/docs/patterns/sqlite3.rst index 660b1a0e..5181fea6 100644 --- a/docs/patterns/sqlite3.rst +++ b/docs/patterns/sqlite3.rst @@ -17,7 +17,7 @@ Here is a simple example of how you can use SQLite 3 with Flask:: def get_db(): db = getattr(g, '_database', None) if db is None: - db = g._database = connect_to_database() + db = g._database = sqlite3.connect(DATABASE) return db @app.teardown_appcontext From d094d5b6fc86a26680a8c496f719c4d711fcd766 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 8 Feb 2014 22:29:46 +0000 Subject: [PATCH 0105/2502] Fixed warning and broken tests on 3.3 --- flask/testsuite/helpers.py | 5 +++-- flask/testsuite/testing.py | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index c645dc14..b765528b 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -265,8 +265,9 @@ class SendfileTestCase(FlaskTestCase): # etags self.assert_equal(len(captured), 1) with catch_warnings() as captured: - from StringIO import StringIO as PYStringIO - f = PYStringIO('Test') + class PyStringIO(StringIO): + pass + f = PyStringIO('Test') f.name = 'test.txt' rv = flask.send_file(f) rv.direct_passthrough = False diff --git a/flask/testsuite/testing.py b/flask/testsuite/testing.py index de78746b..11ab763b 100644 --- a/flask/testsuite/testing.py +++ b/flask/testsuite/testing.py @@ -207,8 +207,8 @@ class TestToolsTestCase(FlaskTestCase): with app.test_client() as c: rv = c.post('http://domain.com/action?vodka=42', data={'gin': 43}) self.assert_equal(rv.status_code, 200) - self.assert_('gin' in flask.request.form) - self.assert_('vodka' in flask.request.args) + self.assert_true('gin' in flask.request.form) + self.assert_true('vodka' in flask.request.args) class SubdomainTestCase(FlaskTestCase): From 76e2f9cd0396c71eedf7e3b1dabf60f36585722e Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 13:01:54 +0000 Subject: [PATCH 0106/2502] Fixed send_from_directory not rebasing to the root path. This fixes #921 --- CHANGES | 2 ++ flask/helpers.py | 2 ++ 2 files changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index b66432af..fc5831b5 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,8 @@ Version 0.10.2 the test client when absolute URLs were requested. - Made `@before_first_request` into a decorator as intended. - Fixed an etags bug when sending a file streams with a name. +- Fixed `send_from_directory` not expanding to the application root path + correctly. Version 0.10.1 -------------- diff --git a/flask/helpers.py b/flask/helpers.py index 7b69b63c..3e303305 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -615,6 +615,8 @@ def send_from_directory(directory, filename, **options): forwarded to :func:`send_file`. """ filename = safe_join(directory, filename) + if not os.path.isabs(filename): + filename = os.path.join(current_app.root_path, filename) if not os.path.isfile(filename): raise NotFound() options.setdefault('conditional', True) From 8cb79fd026038fc63c8525a0aa0602a0f2a7e409 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 13:06:54 +0000 Subject: [PATCH 0107/2502] Added a test for sending from directories --- flask/testsuite/helpers.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index b765528b..d0beee04 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -368,6 +368,16 @@ class SendfileTestCase(FlaskTestCase): self.assert_equal(cc.max_age, 10) rv.close() + def test_send_from_directory(self): + app = flask.Flask(__name__) + app.testing = True + app.root_path = os.path.join(os.path.dirname(__file__), + 'test_apps', 'subdomaintestmodule') + with app.test_request_context(): + rv = flask.send_from_directory('static', 'hello.txt') + rv.direct_passthrough = False + self.assert_equal(rv.get_data().strip(), b'Hello Subdomain') + class LoggingTestCase(FlaskTestCase): From 001100bc0b3a700540024c410a985c7f9e09f565 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 13:13:31 +0000 Subject: [PATCH 0108/2502] Fixed PyStringIO in tests for 2.x --- flask/testsuite/helpers.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index d0beee04..cc377587 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -265,8 +265,11 @@ class SendfileTestCase(FlaskTestCase): # etags self.assert_equal(len(captured), 1) with catch_warnings() as captured: - class PyStringIO(StringIO): - pass + class PyStringIO(object): + def __init__(self, *args, **kwargs): + self._io = StringIO(*args, **kwargs) + def __getattr__(self, name): + return getattr(self._io, name) f = PyStringIO('Test') f.name = 'test.txt' rv = flask.send_file(f) From 964c4a37c071aa312770c4ef51c025516d28a646 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 13:14:15 +0000 Subject: [PATCH 0109/2502] Closed an unclosed file in the tests --- flask/testsuite/helpers.py | 1 + 1 file changed, 1 insertion(+) diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index cc377587..3f822433 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -380,6 +380,7 @@ class SendfileTestCase(FlaskTestCase): rv = flask.send_from_directory('static', 'hello.txt') rv.direct_passthrough = False self.assert_equal(rv.get_data().strip(), b'Hello Subdomain') + rv.close() class LoggingTestCase(FlaskTestCase): From 280d8659601a5e3b8dab77231d9f9fc16f5cd1ce Mon Sep 17 00:00:00 2001 From: Alexis Svinartchouk Date: Fri, 4 Oct 2013 11:09:23 +0200 Subject: [PATCH 0110/2502] fix issue #879 and add a test for it Signed-off-by: Armin Ronacher --- flask/app.py | 2 +- flask/testsuite/basic.py | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index c3170df6..cc3fc1d9 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1492,9 +1492,9 @@ class Flask(_PackageBoundObject): with self._before_request_lock: if self._got_first_request: return - self._got_first_request = True for func in self.before_first_request_funcs: func() + self._got_first_request = True def make_default_options_response(self): """This method is called to create the default `OPTIONS` response. diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 1858ca5c..8399a607 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -16,6 +16,7 @@ import pickle import unittest from datetime import datetime from threading import Thread +from time import sleep from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning from flask._compat import text_type from werkzeug.exceptions import BadRequest, NotFound @@ -1015,6 +1016,23 @@ class BasicFunctionalityTestCase(FlaskTestCase): self.assert_equal(got, [42]) self.assert_true(app.got_first_request) + def test_before_first_request_functions_concurrent(self): + got = [] + app = flask.Flask(__name__) + @app.before_first_request + def foo(): + sleep(1) + got.append(42) + c = app.test_client() + def get_and_assert(): + c.get("/") + self.assert_equal(got, [42]) + t = Thread(target=get_and_assert) + t.start() + get_and_assert() + t.join() + self.assert_true(app.got_first_request) + def test_routing_redirect_debugging(self): app = flask.Flask(__name__) app.debug = True From 75f2af7a6afdf620d7afdccee876b06f8c39373b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 13:25:36 +0000 Subject: [PATCH 0111/2502] Use a condition variable instead of sleeping --- flask/testsuite/basic.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 8399a607..4da4c549 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -15,8 +15,7 @@ import flask import pickle import unittest from datetime import datetime -from threading import Thread -from time import sleep +from threading import Thread, Condition from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning from flask._compat import text_type from werkzeug.exceptions import BadRequest, NotFound @@ -1019,12 +1018,16 @@ class BasicFunctionalityTestCase(FlaskTestCase): def test_before_first_request_functions_concurrent(self): got = [] app = flask.Flask(__name__) + cv = Condition() @app.before_first_request def foo(): - sleep(1) + with cv: + cv.wait() got.append(42) c = app.test_client() def get_and_assert(): + with cv: + cv.notify() c.get("/") self.assert_equal(got, [42]) t = Thread(target=get_and_assert) From 141a5533c97306525551dc215510735e5b50ae0b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 13:26:32 +0000 Subject: [PATCH 0112/2502] Documented fix for #879 --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index fc5831b5..a67937ff 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,9 @@ Version 0.10.2 - Fixed an etags bug when sending a file streams with a name. - Fixed `send_from_directory` not expanding to the application root path correctly. +- Changed logic of before first request handlers to flip the flag after + invoking. This will allow some uses that are potentially dangerous but + should probably be permitted. Version 0.10.1 -------------- From fb622380d07372cc8c24b117bae53f45acfd1878 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 13:28:57 +0000 Subject: [PATCH 0113/2502] Use older descriptor to support older werkzeug versions --- flask/testsuite/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/testsuite/helpers.py b/flask/testsuite/helpers.py index 3f822433..e2bdda1e 100644 --- a/flask/testsuite/helpers.py +++ b/flask/testsuite/helpers.py @@ -379,7 +379,7 @@ class SendfileTestCase(FlaskTestCase): with app.test_request_context(): rv = flask.send_from_directory('static', 'hello.txt') rv.direct_passthrough = False - self.assert_equal(rv.get_data().strip(), b'Hello Subdomain') + self.assert_equal(rv.data.strip(), b'Hello Subdomain') rv.close() From 82f99f42a829b0cc71df92d54e613ce888d805bb Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 13:35:38 +0000 Subject: [PATCH 0114/2502] Disarmed a sentence a bit --- docs/patterns/appfactories.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index 5eeff541..25a90212 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -61,7 +61,7 @@ It's preferable to create your extensions and app factories so that the extension object does not initially get bound to the application. Using `Flask-SQLAlchemy `_, -as an example, you should **not** do:: +as an example, you should not do something along those lines:: def create_app(config_filename): app = Flask(__name__) @@ -82,7 +82,6 @@ and in your application.py (or equivalent):: from yourapplication.model import db db.init_app(app) - Using this design pattern, no application-specific state is stored on the extension object, so one extension object can be used for multiple apps. For more information about the design of extensions refer to :doc:`/extensiondev`. From ddb5f9dc490879236cf20a7fa1cb75d7f787a0f0 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 9 Feb 2014 16:07:56 +0000 Subject: [PATCH 0115/2502] Fixed a broken test --- flask/testsuite/basic.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 4da4c549..31fade13 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -11,11 +11,12 @@ import re import uuid +import time import flask import pickle import unittest from datetime import datetime -from threading import Thread, Condition +from threading import Thread from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning from flask._compat import text_type from werkzeug.exceptions import BadRequest, NotFound @@ -1018,18 +1019,17 @@ class BasicFunctionalityTestCase(FlaskTestCase): def test_before_first_request_functions_concurrent(self): got = [] app = flask.Flask(__name__) - cv = Condition() + @app.before_first_request def foo(): - with cv: - cv.wait() + time.sleep(0.2) got.append(42) + c = app.test_client() def get_and_assert(): - with cv: - cv.notify() c.get("/") self.assert_equal(got, [42]) + t = Thread(target=get_and_assert) t.start() get_and_assert() From a8e88bebd1dff6b982d721c781d87888ef756e4a Mon Sep 17 00:00:00 2001 From: Matt Iversen Date: Tue, 11 Feb 2014 07:24:31 +1100 Subject: [PATCH 0116/2502] Update windows installation and other notes Making allowance for new convenience of `get-pip.py` that automatically installs setuptools and pip together. Stop recommending distribute, which has now been merged into setuptools. --- docs/installation.rst | 68 ++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 30 deletions(-) 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 +--------------------------------- -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. +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. -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:: +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`_ - ;C:\Python27\Scripts +To install the latest setuptools, you can use its bootstrap file: -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. +`ez_setup.py`_ -Now that you have ``easy_install``, you can use it to install ``pip``:: +Either should be double-clickable once you download them. If you already have pip, +you can upgrade them by running:: - > easy_install pip + > pip install --upgrade pip setuptools +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!). -.. _distribute_setup.py: http://python-distribute.org/distribute_setup.py +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. + +Finally, to install `virtualenv`_, you can simply run:: + + > pip install virtualenv + +Then you can be off on your way following the installation instructions above. + +.. _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 From 97411295e3b46080f8976f14e73a6493ca17a2db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Wed, 12 Feb 2014 23:53:51 +0100 Subject: [PATCH 0117/2502] Add Config.from_json to changelog --- CHANGES | 1 + flask/config.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index d646383e..69648f23 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,7 @@ Version 1.0 - Made Flask support custom JSON mimetypes for incoming data. - Added support for returning tuples in the form ``(response, headers)`` from a view function. +- Added :meth:`flask.Config.from_json`. Version 0.10.2 -------------- diff --git a/flask/config.py b/flask/config.py index 41909857..2ed5c180 100644 --- a/flask/config.py +++ b/flask/config.py @@ -175,6 +175,8 @@ class Config(dict): root path. :param silent: set to `True` if you want silent failure for missing files. + + .. versionadded:: 1.0 """ filename = os.path.join(self.root_path, filename) From 19baae3d3445344679b68436322de08b5babf9a1 Mon Sep 17 00:00:00 2001 From: Joe Friedl Date: Thu, 13 Feb 2014 10:28:21 -0500 Subject: [PATCH 0118/2502] Update docs copyright date Happy belated New Year! --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index feed359f..16c841f4 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,7 +43,7 @@ master_doc = 'index' # General information about the project. project = u'Flask' -copyright = u'2013, Armin Ronacher' +copyright = u'2014, Armin Ronacher' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the From 3f8e29b12cd2b909d4dbbc3964339fd3f56c3203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Thu, 20 Feb 2014 19:15:42 +0100 Subject: [PATCH 0119/2502] Add Flask.config_class to changelog --- CHANGES | 1 + flask/app.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 69648f23..c6d252af 100644 --- a/CHANGES +++ b/CHANGES @@ -18,6 +18,7 @@ Version 1.0 - Added support for returning tuples in the form ``(response, headers)`` from a view function. - Added :meth:`flask.Config.from_json`. +- Added :attr:`flask.Flask.config_class`. Version 0.10.2 -------------- diff --git a/flask/app.py b/flask/app.py index 9222540a..257c5e93 100644 --- a/flask/app.py +++ b/flask/app.py @@ -182,6 +182,8 @@ class Flask(_PackageBoundObject): #: #: 1. Default values for certain config options. #: 2. Access to config values through attributes in addition to keys. + #: + #: .. versionadded:: 1.0 config_class = Config #: The debug flag. Set this to `True` to enable debugging of the From d43bfb261a0cb562fdfec6d94768c80d95844578 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Thu, 20 Feb 2014 19:17:59 +0100 Subject: [PATCH 0120/2502] Add assertIsInstance to FlaskTestCase on 2.6 This fixes an error in ConfigTestCase.test_custom_config_class --- flask/testsuite/__init__.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flask/testsuite/__init__.py b/flask/testsuite/__init__.py index 8a6d1477..d0d62f29 100644 --- a/flask/testsuite/__init__.py +++ b/flask/testsuite/__init__.py @@ -167,6 +167,9 @@ class FlaskTestCase(unittest.TestCase): def assertNotIn(self, x, y): assert x not in y, "%r unexpectedly in %r" % (x, y) + def assertIsInstance(self, x, y): + assert isinstance(x, y), "not isinstance(%r, %r)" % (x, y) + class _ExceptionCatcher(object): From ffff509cf07b4791201915f98116aec51eb4a651 Mon Sep 17 00:00:00 2001 From: Eliseo Ocampos Date: Tue, 4 Mar 2014 14:57:12 -0800 Subject: [PATCH 0121/2502] Added 'import os' statement Added 'import os' statement so you can use os.path.join() when defining DATABASE location --- docs/tutorial/setup.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index ed8a744c..65d4b3f2 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -14,6 +14,7 @@ load that or import the values from there. First we add the imports in `flaskr.py`:: # all the imports + import os import sqlite3 from flask import Flask, request, session, g, redirect, url_for, abort, \ render_template, flash From dfae2679a6be1d3e61eaa9f83e85e0daf655187c Mon Sep 17 00:00:00 2001 From: Charles-Axel Dein Date: Wed, 5 Mar 2014 15:22:59 -0800 Subject: [PATCH 0122/2502] Clarify the after_request argument Make it a bit clearer that it's an instance of response_class that is expected, not the actual response_class class. --- flask/app.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/flask/app.py b/flask/app.py index 257c5e93..2bff6af0 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1219,9 +1219,11 @@ class Flask(_PackageBoundObject): @setupmethod def after_request(self, f): - """Register a function to be run after each request. Your function - must take one parameter, a :attr:`response_class` object and return - a new response object or the same (see :meth:`process_response`). + """Register a function to be run after each request. + + Your function must take one parameter, an instance of + :attr:`response_class` and return a new response object or the + same (see :meth:`process_response`). As of Flask 0.7 this function might not be executed at the end of the request in case an unhandled exception occurred. From ba80e1e33bd93fd895d1b88853f47fb30cd0b670 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Thu, 13 Mar 2014 20:22:18 +0100 Subject: [PATCH 0123/2502] Fix Python 3 compat issue in Config.get_namespace --- flask/config.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/config.py b/flask/config.py index cb1c9bb8..164a04ba 100644 --- a/flask/config.py +++ b/flask/config.py @@ -14,7 +14,7 @@ import os import errno from werkzeug.utils import import_string -from ._compat import string_types +from ._compat import string_types, iteritems from . import json @@ -218,7 +218,7 @@ class Config(dict): dictionary should be lowercase """ rv = {} - for k, v in self.iteritems(): + for k, v in iteritems(self): if not k.startswith(namespace): continue key = k[len(namespace):] From 06857c9ba58fb3b93daa3604538ccd3fd2f1d07e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Thu, 13 Mar 2014 20:23:43 +0100 Subject: [PATCH 0124/2502] Add Config.get_namespace to CHANGES --- CHANGES | 1 + flask/config.py | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index c6d252af..a6fa7ec6 100644 --- a/CHANGES +++ b/CHANGES @@ -19,6 +19,7 @@ Version 1.0 from a view function. - Added :meth:`flask.Config.from_json`. - Added :attr:`flask.Flask.config_class`. +- Added :meth:`flask.config.Config.get_namespace`. Version 0.10.2 -------------- diff --git a/flask/config.py b/flask/config.py index 164a04ba..dfc2f6b3 100644 --- a/flask/config.py +++ b/flask/config.py @@ -216,6 +216,8 @@ class Config(dict): :param namespace: a configuration namespace :param lowercase: a flag indicating if the keys of the resulting dictionary should be lowercase + + .. versionadded:: 1.0 """ rv = {} for k, v in iteritems(self): From cb25c42316690860fd928d9ec6fc997eea3f062b Mon Sep 17 00:00:00 2001 From: max demian Date: Sat, 15 Mar 2014 14:35:21 +0100 Subject: [PATCH 0125/2502] Update json.py --- flask/json.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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('/') From f3f7b640b979a3d9f298c25ee42c72e7bb44b479 Mon Sep 17 00:00:00 2001 From: max demian Date: Sat, 15 Mar 2014 14:38:19 +0100 Subject: [PATCH 0126/2502] Update sessions.py missing backticks in docstring --- flask/sessions.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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`. """ From 2eef63a3208ebc5ee69a7feebb57090de3982110 Mon Sep 17 00:00:00 2001 From: max demian Date: Sat, 15 Mar 2014 14:42:21 +0100 Subject: [PATCH 0127/2502] Update signals.py --- flask/signals.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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') From 6974d52815da9a1a00b49191b161e3bd12051bac Mon Sep 17 00:00:00 2001 From: max demian Date: Sat, 15 Mar 2014 14:44:27 +0100 Subject: [PATCH 0128/2502] Update views.py --- flask/views.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/flask/views.py b/flask/views.py index 607cb7a4..39642028 100644 --- a/flask/views.py +++ b/flask/views.py @@ -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 From db12a8f5e4285cda4e7440b6a4c015632cb2b345 Mon Sep 17 00:00:00 2001 From: max demian Date: Sat, 15 Mar 2014 14:45:37 +0100 Subject: [PATCH 0129/2502] Update wrappers.py --- flask/wrappers.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 \ From 8cd66bf178cc72f5718f4113db9426723fe37ef5 Mon Sep 17 00:00:00 2001 From: Kyle Stevenson Date: Wed, 19 Mar 2014 10:23:17 -0700 Subject: [PATCH 0130/2502] Small punctuation change in unicode doc Just makes it easier on the eyes/brain. --- docs/unicode.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 95fa9f21ec43046c113992e89a2741505e666597 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 19 Mar 2014 23:07:42 +0000 Subject: [PATCH 0131/2502] Added a missing import --- docs/tutorial/setup.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index ed8a744c..65d4b3f2 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -14,6 +14,7 @@ load that or import the values from there. First we add the imports in `flaskr.py`:: # all the imports + import os import sqlite3 from flask import Flask, request, session, g, redirect, url_for, abort, \ render_template, flash From 2aa26fff04a98f2435e8e3015e92ef6108d3b485 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Fri, 21 Mar 2014 17:12:26 -0400 Subject: [PATCH 0132/2502] Revert "Merge pull request #859 from wvh/register_error_handler" This reverts commit 6347e75deea2eda444c60080fda3885c1fb7705d, reversing changes made to 3fd40e83e5df4dbde8531a76c42d26bf76674e95. --- flask/blueprints.py | 11 ----------- flask/testsuite/blueprints.py | 33 --------------------------------- 2 files changed, 44 deletions(-) diff --git a/flask/blueprints.py b/flask/blueprints.py index 4abc0ffa..45faf2c5 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -399,14 +399,3 @@ 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/testsuite/blueprints.py b/flask/testsuite/blueprints.py index 8b3e530c..8a0e5a79 100644 --- a/flask/testsuite/blueprints.py +++ b/flask/testsuite/blueprints.py @@ -296,39 +296,6 @@ 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__) From d4fec1454488687b574c545761c2869d3bb97ca6 Mon Sep 17 00:00:00 2001 From: Kenneth Reitz Date: Fri, 21 Mar 2014 17:15:31 -0400 Subject: [PATCH 0133/2502] Revert "Revert "Merge pull request #859 from wvh/register_error_handler"" This reverts commit 2aa26fff04a98f2435e8e3015e92ef6108d3b485. --- flask/blueprints.py | 11 +++++++++++ flask/testsuite/blueprints.py | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) 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/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__) From db1be12aea8193487e7f3097d03ea81780525069 Mon Sep 17 00:00:00 2001 From: Andrew Plummer Date: Wed, 26 Mar 2014 16:40:25 +0000 Subject: [PATCH 0134/2502] Fix import in viewdecorators.rst for @templated --- docs/patterns/viewdecorators.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/viewdecorators.rst b/docs/patterns/viewdecorators.rst index a0948577..46bab334 100644 --- a/docs/patterns/viewdecorators.rst +++ b/docs/patterns/viewdecorators.rst @@ -127,7 +127,7 @@ way you can still use the redirect function or return simple strings. Here the code for that decorator:: from functools import wraps - from flask import request + from flask import request, render_template def templated(template=None): def decorator(f): From b2b531a36bab939f76df51b7ae88f87e60eea8f6 Mon Sep 17 00:00:00 2001 From: Andrew Plummer Date: Wed, 26 Mar 2014 16:52:06 +0000 Subject: [PATCH 0135/2502] Tweak wording in viewdecorators.rst --- docs/patterns/viewdecorators.rst | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/patterns/viewdecorators.rst b/docs/patterns/viewdecorators.rst index 46bab334..0e523cfd 100644 --- a/docs/patterns/viewdecorators.rst +++ b/docs/patterns/viewdecorators.rst @@ -2,12 +2,12 @@ View Decorators =============== Python has a really interesting feature called function decorators. This -allow some really neat things for web applications. Because each view in -Flask is a function decorators can be used to inject additional +allows some really neat things for web applications. Because each view in +Flask is a function, decorators can be used to inject additional functionality to one or more functions. The :meth:`~flask.Flask.route` decorator is the one you probably used already. But there are use cases for implementing your own decorator. For instance, imagine you have a -view that should only be used by people that are logged in to. If a user +view that should only be used by people that are logged in. If a user goes to the site and is not logged in, they should be redirected to the login page. This is a good example of a use case where a decorator is an excellent solution. @@ -54,7 +54,7 @@ because of that you would like to cache the generated results for a certain amount of time. A decorator would be nice for that. We're assuming you have set up a cache like mentioned in :ref:`caching-pattern`. -Here an example cache function. It generates the cache key from a +Here is an example cache function. It generates the cache key from a specific prefix (actually a format string) and the current path of the request. Notice that we are using a function that first creates the decorator that then decorates the function. Sounds awful? Unfortunately @@ -124,7 +124,7 @@ the dictionary returned is passed to the template rendering function. If a dictionary is returned we return it from the function unchanged. That way you can still use the redirect function or return simple strings. -Here the code for that decorator:: +Here is the code for that decorator:: from functools import wraps from flask import request, render_template @@ -163,6 +163,3 @@ to a view function. This is possible with this decorator. For example:: @app.endpoint('index') def my_index(): return "Hello world" - - - From 132c2c4c795ebdccc6f347867092b30b5f5086bb Mon Sep 17 00:00:00 2001 From: atupal Date: Wed, 2 Apr 2014 09:16:43 +0800 Subject: [PATCH 0136/2502] missed word. request tstack -> request context stack --- docs/upgrading.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/upgrading.rst b/docs/upgrading.rst index 4a0676a8..ebea0101 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -64,8 +64,8 @@ If you maintain an extension that was using :data:`~flask._request_ctx_stack` before, please consider changing to :data:`~flask._app_ctx_stack` if it makes sense for your extension. For instance, the app context stack makes sense for extensions which connect to databases. Using the app context stack instead of -the request stack will make extensions more readily handle use cases outside of -requests. +the request context stack will make extensions more readily handle use cases +outside of requests. Version 0.8 ----------- From 54688b26a9562c9a9c31541dbf32e0d8ee7a5926 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 5 Apr 2014 11:38:28 +0200 Subject: [PATCH 0137/2502] Fix when release date is not today Also, some fixes for PyFlakes Original commit by @dongweiming at a05c4038706f3a95776adea376bc664c0714b426 --- scripts/make-release.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) 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') From f79ce525866a0cb70a52c706ee56532a05707f22 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 10 Apr 2014 00:27:27 +0200 Subject: [PATCH 0138/2502] Upload to new server --- Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index b67c8a9f..98e74135 100644 --- a/Makefile +++ b/Makefile @@ -29,10 +29,10 @@ upload-docs: $(MAKE) -C docs html dirhtml latex epub $(MAKE) -C docs/_build/latex all-pdf cd docs/_build/; mv html flask-docs; zip -r flask-docs.zip flask-docs; mv flask-docs html - rsync -a docs/_build/dirhtml/ pocoo.org:/var/www/flask.pocoo.org/docs/ - rsync -a docs/_build/latex/Flask.pdf pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.pdf - rsync -a docs/_build/flask-docs.zip pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.zip - rsync -a docs/_build/epub/Flask.epub pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.epub + rsync -a docs/_build/dirhtml/ flow.srv.pocoo.org:/srv/websites/flask.pocoo.org/docs/ + rsync -a docs/_build/latex/Flask.pdf flow.srv.pocoo.org:/srv/websites/flask.pocoo.org/docs/flask-docs.pdf + rsync -a docs/_build/flask-docs.zip flow.srv.pocoo.org:/srv/websites/flask.pocoo.org/docs/flask-docs.zip + rsync -a docs/_build/epub/Flask.epub flow.srv.pocoo.org:/srv/websites/flask.pocoo.org/docs/flask-docs.epub # ebook-convert docs: http://manual.calibre-ebook.com/cli/ebook-convert.html ebook: From 696bffc9e9902ed9086c7c1f967714385371cb05 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 11 Apr 2014 13:28:38 +0200 Subject: [PATCH 0139/2502] Updated changelog formatting --- CHANGES | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGES b/CHANGES index 3fb30c6f..60c16907 100644 --- a/CHANGES +++ b/CHANGES @@ -20,7 +20,6 @@ Version 1.0 - Added :meth:`flask.Config.from_json`. - 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. From 9554844e2ac3e50872aab6f7b54c277ff582cd13 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 11 Apr 2014 13:31:17 +0200 Subject: [PATCH 0140/2502] Added workaround for Python 3.3's frozen loader --- CHANGES | 1 + flask/helpers.py | 38 ++++++++++++++++++++++++++++++-------- 2 files changed, 31 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 60c16907..e551dbef 100644 --- a/CHANGES +++ b/CHANGES @@ -23,6 +23,7 @@ Version 1.0 - 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. +- Added a workaround for a limitation in Python 3.3's namespace loader. Version 0.10.2 -------------- diff --git a/flask/helpers.py b/flask/helpers.py index 4cced215..4e80bcd6 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -656,6 +656,29 @@ def get_root_path(import_name): return os.path.dirname(os.path.abspath(filepath)) +def _matching_loader_thinks_module_is_package(loader, mod_name): + """Given the loader that loaded a module and the module this function + attempts to figure out if the given module is actually a package. + """ + # If the loader can tell us if something is a package, we can + # directly ask the loader. + if hasattr(loader, 'is_package'): + return loader.is_package(mod_name) + # importlib's namespace loaders do not have this functionality but + # all the modules it loads are packages, so we can take advantage of + # this information. + elif (loader.__module__ == '_frozen_importlib' and + loader.__name__ == 'NamespaceLoader'): + return True + # Otherwise we need to fail with an error that explains what went + # wrong. + raise AttributeError( + ('%s.is_package() method is missing but is required by Flask of ' + 'PEP 302 import hooks. If you do not use import hooks and ' + 'you encounter this error please file a bug against Flask.') % + loader.__class__.__name__) + + def find_package(import_name): """Finds a package and returns the prefix (or None if the package is not installed) as well as the folder that contains the package or @@ -685,14 +708,13 @@ def find_package(import_name): __import__(import_name) filename = sys.modules[import_name].__file__ package_path = os.path.abspath(os.path.dirname(filename)) - # package_path ends with __init__.py for a package - if hasattr(loader, 'is_package'): - if loader.is_package(root_mod_name): - package_path = os.path.dirname(package_path) - else: - raise AttributeError( - ('%s.is_package() method is missing but is ' - 'required by Flask of PEP 302 import hooks') % loader.__class__.__name__) + + # In case the root module is a pcakage we need to chop of the + # rightmost part. This needs to go through a helper function + # because of python 3.3 namespace packages. + if _matching_loader_thinks_module_is_package( + loader, root_mod_name): + package_path = os.path.dirname(package_path) site_parent, site_folder = os.path.split(package_path) py_prefix = os.path.abspath(sys.prefix) From a293b99b219c13ea94b33a2470a545e07c73b804 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 11 Apr 2014 13:31:57 +0200 Subject: [PATCH 0141/2502] Fixed the last changeset. --- flask/helpers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index 4e80bcd6..cd321e57 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -667,8 +667,8 @@ def _matching_loader_thinks_module_is_package(loader, mod_name): # importlib's namespace loaders do not have this functionality but # all the modules it loads are packages, so we can take advantage of # this information. - elif (loader.__module__ == '_frozen_importlib' and - loader.__name__ == 'NamespaceLoader'): + elif (loader.__class__.__module__ == '_frozen_importlib' and + loader.__class__.__name__ == 'NamespaceLoader'): return True # Otherwise we need to fail with an error that explains what went # wrong. From d4b3d16c142e2189c6faf8f784a195e7f827c596 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 11 Apr 2014 19:59:54 +0200 Subject: [PATCH 0142/2502] Better support for namespace packages. --- CHANGES | 2 ++ flask/app.py | 14 ++++++++++++-- flask/blueprints.py | 6 ++++-- flask/helpers.py | 22 +++++++++++++++++++--- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index e551dbef..8fc7670b 100644 --- a/CHANGES +++ b/CHANGES @@ -24,6 +24,8 @@ Version 1.0 templates will be reloaded only if the application is running in debug mode. For higher performance it’s possible to disable that. - Added a workaround for a limitation in Python 3.3's namespace loader. +- Added support for explicit root paths when using Python 3.3's namespace + packages. Version 0.10.2 -------------- diff --git a/flask/app.py b/flask/app.py index 8d59fd30..6b6da433 100644 --- a/flask/app.py +++ b/flask/app.py @@ -118,6 +118,9 @@ class Flask(_PackageBoundObject): The `instance_path` and `instance_relative_config` parameters were added. + .. versionadded:: 1.0 + The `root_path` parameter was added. + :param import_name: the name of the application package :param static_url_path: can be used to specify a different path for the static files on the web. Defaults to the name @@ -137,6 +140,11 @@ class Flask(_PackageBoundObject): for loading the config are assumed to be relative to the instance path instead of the application root. + :param root_path: Flask by default will automatically calculate the path + to the root of the application. In certain situations + this cannot be achieved (for instance if the package + is a Python 3 namespace package) and needs to be + manually defined. """ #: The class that is used for request objects. See :class:`~flask.Request` @@ -327,9 +335,11 @@ class Flask(_PackageBoundObject): def __init__(self, import_name, static_path=None, static_url_path=None, static_folder='static', template_folder='templates', - instance_path=None, instance_relative_config=False): + instance_path=None, instance_relative_config=False, + root_path=None): _PackageBoundObject.__init__(self, import_name, - template_folder=template_folder) + template_folder=template_folder, + root_path=root_path) if static_path is not None: from warnings import warn warn(DeprecationWarning('static_path is now called ' diff --git a/flask/blueprints.py b/flask/blueprints.py index 4abc0ffa..924c5441 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -91,8 +91,10 @@ class Blueprint(_PackageBoundObject): def __init__(self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, - url_prefix=None, subdomain=None, url_defaults=None): - _PackageBoundObject.__init__(self, import_name, template_folder) + url_prefix=None, subdomain=None, url_defaults=None, + root_path=None): + _PackageBoundObject.__init__(self, import_name, template_folder, + root_path=root_path) self.name = name self.url_prefix = url_prefix self.subdomain = subdomain diff --git a/flask/helpers.py b/flask/helpers.py index cd321e57..920c4c87 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -650,7 +650,20 @@ def get_root_path(import_name): else: # Fall back to imports. __import__(import_name) - filepath = sys.modules[import_name].__file__ + mod = sys.modules[import_name] + filepath = getattr(mod, '__file__', None) + + # If we don't have a filepath it might be because we are a + # namespace package. In this case we pick the root path from the + # first module that is contained in our package. + if filepath is None: + raise RuntimeError('No root path can be found for the provided ' + 'module "%s". This can happen because the ' + 'module came from an import hook that does ' + 'not provide file name information or because ' + 'it\'s a namespace package. In this case ' + 'the root path needs to be explictly ' + 'provided.' % import_name) # filepath is import_name.py for a module, or __init__.py for a package. return os.path.dirname(os.path.abspath(filepath)) @@ -762,7 +775,7 @@ class locked_cached_property(object): class _PackageBoundObject(object): - def __init__(self, import_name, template_folder=None): + def __init__(self, import_name, template_folder=None, root_path=None): #: The name of the package or module. Do not change this once #: it was set by the constructor. self.import_name = import_name @@ -771,8 +784,11 @@ class _PackageBoundObject(object): #: exposed. self.template_folder = template_folder + if root_path is None: + root_path = get_root_path(self.import_name) + #: Where is the app root located? - self.root_path = get_root_path(self.import_name) + self.root_path = root_path self._static_folder = None self._static_url_path = None From fbda3f94baa6d7dba8f773309121284c6e6c5a6d Mon Sep 17 00:00:00 2001 From: raimu Date: Fri, 11 Apr 2014 22:40:21 +0200 Subject: [PATCH 0143/2502] fix typo --- flask/json.py | 2 +- flask/wrappers.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/json.py b/flask/json.py index bdebf707..a0ed7041 100644 --- a/flask/json.py +++ b/flask/json.py @@ -119,7 +119,7 @@ def dumps(obj, **kwargs): This function can return ``unicode`` strings or ascii-only bytestrings by default which coerce into unicode strings automatically. That behavior by default is controlled by the ``JSON_AS_ASCII`` configuration variable - and can be overriden by the simplejson ``ensure_ascii`` parameter. + and can be overridden by the simplejson ``ensure_ascii`` parameter. """ _dump_arg_defaults(kwargs) encoding = kwargs.pop('encoding', None) diff --git a/flask/wrappers.py b/flask/wrappers.py index 528e94f5..f74a3f37 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -127,7 +127,7 @@ class Request(RequestBase): parsing fails the :meth:`on_json_loading_failed` method on the request object will be invoked. By default this function will only load the json data if the mimetype is ``application/json`` - but this can be overriden by the `force` parameter. + but this can be overridden by the `force` parameter. :param force: if set to `True` the mimetype is ignored. :param silent: if set to `True` this method will fail silently From 15d8af52dbb091f198664bedc5dfc3d5d9ffdea0 Mon Sep 17 00:00:00 2001 From: mjfroehlich Date: Sun, 13 Apr 2014 22:25:10 +0200 Subject: [PATCH 0144/2502] Fix minor typo --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index 6b6da433..bb5cbbd7 100644 --- a/flask/app.py +++ b/flask/app.py @@ -874,7 +874,7 @@ class Flask(_PackageBoundObject): return self.session_interface.make_null_session(self) def register_module(self, module, **options): - """Registers a module with this application. The keyword argument + """Registers a module with this application. The keyword arguments of this function are the same as the ones for the constructor of the :class:`Module` class and will override the values of the module if provided. From 671c67aac96ae2cdf9c554ba4e52c167862be6fe Mon Sep 17 00:00:00 2001 From: Mihir Singh Date: Wed, 16 Apr 2014 17:38:41 -0400 Subject: [PATCH 0145/2502] Append a 'Vary: Cookie' header to the response when the session has been accessed --- flask/sessions.py | 56 +++++++++++++++++++++++++++++++++-------------- 1 file changed, 39 insertions(+), 17 deletions(-) diff --git a/flask/sessions.py b/flask/sessions.py index 82ba3506..99e3980d 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -51,6 +51,13 @@ class SessionMixin(object): #: The default mixin implementation just hardcodes `True` in. modified = True + #: the accessed variable indicates whether or not the session object has + #: been accessed in that request. This allows flask to append a `Vary: + #: Cookie` header to the response if the session is being accessed. This + #: allows caching proxy servers, like Varnish, to use both the URL and the + #: session cookie as keys when caching pages, preventing multiple users + #: from being served the same cache. + accessed = True class TaggedJSONSerializer(object): """A customized JSON serializer that supports a few extra types that @@ -112,9 +119,18 @@ class SecureCookieSession(CallbackDict, SessionMixin): def __init__(self, initial=None): def on_update(self): self.modified = True + self.accessed = True CallbackDict.__init__(self, initial, on_update) self.modified = False + self.accessed = False + def __getitem__(self, key): + self.accessed = True + return super(SecureCookieSession, self).__getitem__(key) + + def get(self, key, default=None): + self.accessed = True + return super(SecureCookieSession, self).get(key, default) class NullSession(SecureCookieSession): """Class used to generate nicer error messages if sessions are not @@ -334,24 +350,30 @@ class SecureCookieSessionInterface(SessionInterface): domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) - # Delete case. If there is no session we bail early. - # If the session was modified to be empty we remove the - # whole cookie. - if not session: - if session.modified: - response.delete_cookie(app.session_cookie_name, - domain=domain, path=path) - return + if session.accessed: - # Modification case. There are upsides and downsides to - # emitting a set-cookie header each request. The behavior - # is controlled by the :meth:`should_set_cookie` method - # which performs a quick check to figure out if the cookie - # should be set or not. This is controlled by the - # SESSION_REFRESH_EACH_REQUEST config flag as well as - # the permanent flag on the session itself. - if not self.should_set_cookie(app, session): - return + response.headers.add('Vary', 'Cookie') + + else: + + # Delete case. If there is no session we bail early. + # If the session was modified to be empty we remove the + # whole cookie. + if not session: + if session.modified: + response.delete_cookie(app.session_cookie_name, + domain=domain, path=path) + return + + # Modification case. There are upsides and downsides to + # emitting a set-cookie header each request. The behavior + # is controlled by the :meth:`should_set_cookie` method + # which performs a quick check to figure out if the cookie + # should be set or not. This is controlled by the + # SESSION_REFRESH_EACH_REQUEST config flag as well as + # the permanent flag on the session itself. + if not self.should_set_cookie(app, session): + return httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) From 5935ee495c3e51d26fa2b33de09d532b310cd263 Mon Sep 17 00:00:00 2001 From: Mihir Singh Date: Wed, 16 Apr 2014 17:39:11 -0400 Subject: [PATCH 0146/2502] Add tests for the `Vary: Cookie` header --- flask/testsuite/basic.py | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index bc0838ad..4ab0bbb3 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -396,6 +396,56 @@ class BasicFunctionalityTestCase(FlaskTestCase): app.config['SESSION_REFRESH_EACH_REQUEST'] = False run_test(expect_header=False) + def test_session_vary_cookie(self): + + app = flask.Flask(__name__) + app.secret_key = 'testkey' + + @app.route('/set-session') + def set_session(): + + flask.session['test'] = 'test' + return '' + + @app.route('/get-session') + def get_session(): + + s = flask.session.get('test') + return '' + + @app.route('/get-session-with-dictionary') + def get_session_with_dictionary(): + + s = flask.session['test'] + return '' + + @app.route('/no-vary-header') + def no_vary_header(): + + return '' + + c = app.test_client() + + rv = c.get('/set-session') + + self.assert_in('Vary', rv.headers) + self.assert_equal('Cookie', rv.headers['Vary']) + + rv = c.get('/get-session') + + self.assert_in('Vary', rv.headers) + self.assert_equal('Cookie', rv.headers['Vary']) + + rv = c.get('/get-session-with-dictionary') + + self.assert_in('Vary', rv.headers) + self.assert_equal('Cookie', rv.headers['Vary']) + + rv = c.get('/no-vary-header') + + self.assert_not_in('Vary', rv.headers) + + def test_flashes(self): app = flask.Flask(__name__) app.secret_key = 'testkey' From 42c28c3758fc1612a5a1e89a36092580344c4578 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 15:58:27 +0200 Subject: [PATCH 0147/2502] Fixed intention in flask init --- flask/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/__init__.py b/flask/__init__.py index bf4774dc..f1560879 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -21,8 +21,8 @@ from jinja2 import Markup, escape from .app import Flask, Request, Response from .config import Config from .helpers import url_for, flash, send_file, send_from_directory, \ - get_flashed_messages, get_template_attribute, make_response, safe_join, \ - stream_with_context + get_flashed_messages, get_template_attribute, make_response, safe_join, \ + stream_with_context from .globals import current_app, g, request, session, _request_ctx_stack, \ _app_ctx_stack from .ctx import has_request_context, has_app_context, \ From 7503bde2235c28853d4c4e53087090d8c42041aa Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 16:34:17 +0200 Subject: [PATCH 0148/2502] Added the new flask run module to start the server. --- flask/run.py | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 4 + 2 files changed, 224 insertions(+) create mode 100644 flask/run.py diff --git a/flask/run.py b/flask/run.py new file mode 100644 index 00000000..cbf3c909 --- /dev/null +++ b/flask/run.py @@ -0,0 +1,220 @@ +import os +import sys +from threading import Lock +from optparse import OptionParser + +from werkzeug.serving import run_simple + +from ._compat import iteritems + + +def find_best_app(module): + """Given a module instance this tries to find the best possible application + in the module or raises a RuntimeError. + """ + from flask import Flask + + # The app name wins, even if it's not a flask object. + app = getattr(module, 'app', None) + if app is not None and callable(app): + return app + + # Otherwise find the first object named Flask + matches = [] + for key, value in iteritems(module.__dict__): + if isinstance(value, Flask): + matches.append(value) + + if matches: + if len(matches) > 1: + raise RuntimeError('More than one possible Flask application ' + 'found in module "%s", none of which are called ' + '"app". Be explicit!' % module) + return matches[0] + + raise RuntimeError('Failed to find application in module "%s". Are ' + 'you sure it contains a Flask application? Maybe ' + 'you wrapped it in a WSGI middleware or you are ' + 'using a factory function.' % module) + + +def prepare_exec_for_file(filename): + module = [] + + # Chop off file extensions or package markers + if filename.endswith('.py'): + filename = filename[:-3] + elif os.path.split(filename)[1] == '__init__.py': + filename = os.path.dirname(filename) + filename = os.path.realpath(filename) + + dirpath = filename + while 1: + dirpath, extra = os.path.split(dirpath) + module.append(extra) + if not os.path.isfile(os.path.join(dirpath, '__init__.py')): + break + + sys.path.insert(0, dirpath) + return '.'.join(module[::-1]) + + +def locate_app(app_id, debug=None): + """Attempts to locate the application.""" + if ':' in app_id: + module, app_obj = app_id.split(':', 1) + else: + module = app_id + app_obj = None + + __import__(module) + mod = sys.modules[module] + if app_obj is None: + app = find_best_app(mod) + else: + app = getattr(mod, app_obj, None) + if app is None: + raise RuntimeError('Failed to find application in module "%s"' + % module) + if debug is not None: + app.debug = debug + return app + + +class DispatchingApp(object): + """Special applicationt that dispatches to a flask application which + is imported by name on first request. This is safer than importing + the application upfront because it means that we can forward all + errors for import problems into the browser as error. + """ + + def __init__(self, app_id, debug=None, use_eager_loading=False): + self.app_id = app_id + self.app = None + self.debug = debug + self._lock = Lock() + if use_eager_loading: + self._load_unlocked() + + def _load_unlocked(self): + self.app = rv = locate_app(self.app_id, self.debug) + return rv + + def __call__(self, environ, start_response): + if self.app is not None: + return self.app(environ, start_response) + with self._lock: + if self.app is not None: + rv = self.app + else: + rv = self._load_unlocked() + return rv(environ, start_response) + + +def run_application(app_id, host='127.0.0.1', port=5000, debug=None, + use_reloader=False, use_debugger=False, + use_eager_loading=None, magic_app_id=True): + """Useful function to start a Werkzeug server for an application that + is known by it's import name. By default the app ID can also be a + full file name in which case Flask attempts to reconstruct the import + name from it and do the right thing. + + :param app_id: the import name of the application module. If a colon + is provided, everything afterwards is the application + object name. In case the magic app id is enabled this + can also be a filename. + :param host: the host to bind to. + :param port: the port to bind to. + :param debug: if set to something other than None then the application's + debug flag will be set to this. + :param use_reloader: enables or disables the reloader. + :param use_debugger: enables or disables the builtin debugger. + :param use_eager_loading: enables or disables eager loading. This is + normally conditional to the reloader. + :param magic_app_id: if this is enabled then the app id can also be a + filename instead of an import module and Flask + will attempt to reconstruct the import name. + """ + if magic_app_id: + if os.path.isfile(app_id) or os.sep in app_id or \ + os.altsep is not None and os.altsep in app_id: + app_id = prepare_exec_for_file(app_id) + + if use_eager_loading is None: + use_eager_loading = not use_reloader + + # Extra startup messages. This depends a but on Werkzeug internals to + # not double execute when the reloader kicks in. + if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': + print ' * Serving Flask app "%s"' % app_id + if debug is not None: + print ' * Forcing debug %s' % (debug and 'on' or 'off') + + app = DispatchingApp(app_id, debug, use_eager_loading) + run_simple(host, port, app, use_reloader=use_reloader, + use_debugger=use_debugger) + + +def main(as_module=False): + this_module = __package__ + '.run' + + if as_module: + name = 'python -m ' + this_module + else: + name = 'flask-run' + + parser = OptionParser(usage='%prog [options] module', prog=name) + parser.add_option('--debug', action='store_true', + dest='debug', help='Flip debug flag on. If enabled ' + 'this also affects debugger and reloader defaults.') + parser.add_option('--no-debug', action='store_false', + dest='debug', help='Flip debug flag off.') + parser.add_option('--host', default='127.0.0.1', + help='The host to bind on. (defaults to 127.0.0.1)') + parser.add_option('--port', default=5000, + help='The port to bind on. (defaults to 5000)') + parser.add_option('--with-reloader', action='store_true', + dest='with_reloader', + help='Enable the reloader.') + parser.add_option('--without-reloader', action='store_false', + dest='with_reloader', + help='Disable the reloader.') + parser.add_option('--with-debugger', action='store_true', + dest='with_debugger', + help='Enable the debugger.') + parser.add_option('--without-debugger', action='store_false', + dest='with_debugger', + help='Disable the debugger.') + parser.add_option('--with-eager-loading', action='store_true', + dest='with_eager_loading', + help='Force enable the eager-loading. This makes the ' + 'application load immediately but makes development ' + 'flows harder. It\'s not recommended to enable eager ' + 'loading when the reloader is enabled as it can lead ' + 'to unexpected crashes.') + parser.add_option('--without-eager-loading', action='store_false', + dest='with_eager_loading', + help='Disable the eager-loading.') + opts, args = parser.parse_args() + if len(args) != 1: + parser.error('Expected exactly one argument which is the import ' + 'name of the application.') + + if opts.with_debugger is None: + opts.with_debugger = opts.debug + if opts.with_reloader is None: + opts.with_reloader = opts.debug + + # This module is always executed as "python -m flask.run" and as such + # we need to ensure that we restore the actual command line so that + # the reloader can properly operate. + sys.argv = ['-m', this_module] + sys.argv[1:] + + run_application(args[0], opts.host, opts.port, debug=opts.debug, + use_reloader=opts.with_reloader, + use_debugger=opts.with_debugger, + use_eager_loading=opts.with_eager_loading) + + +if __name__ == '__main__': + main(as_module=True) diff --git a/setup.py b/setup.py index 5ddfe246..0fbf7e8d 100644 --- a/setup.py +++ b/setup.py @@ -106,6 +106,10 @@ setup( 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ], + entry_points=''' + [console_scripts] + flask-run=flask.run:main + ''', cmdclass={'audit': run_audit}, test_suite='flask.testsuite.suite' ) From e46bca4051e51f3812d4a0b25f60f7b7959668c2 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 16:39:22 +0200 Subject: [PATCH 0149/2502] Added thread flag to flask run --- CHANGES | 3 +++ flask/run.py | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 8fc7670b..80c4b27d 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,9 @@ Version 1.0 - Added a workaround for a limitation in Python 3.3's namespace loader. - Added support for explicit root paths when using Python 3.3's namespace packages. +- Added ``flask-run`` and the ``flask.run`` module to start the local + debug server. This is recommended over the old ``flask.run()`` method + as it works faster and more reliable due to a different design. Version 0.10.2 -------------- diff --git a/flask/run.py b/flask/run.py index cbf3c909..89344616 100644 --- a/flask/run.py +++ b/flask/run.py @@ -113,7 +113,8 @@ class DispatchingApp(object): def run_application(app_id, host='127.0.0.1', port=5000, debug=None, use_reloader=False, use_debugger=False, - use_eager_loading=None, magic_app_id=True): + use_eager_loading=None, magic_app_id=True, + **options): """Useful function to start a Werkzeug server for an application that is known by it's import name. By default the app ID can also be a full file name in which case Flask attempts to reconstruct the import @@ -134,6 +135,10 @@ def run_application(app_id, host='127.0.0.1', port=5000, debug=None, :param magic_app_id: if this is enabled then the app id can also be a filename instead of an import module and Flask will attempt to reconstruct the import name. + :param options: the options to be forwarded to the underlying + Werkzeug server. See + :func:`werkzeug.serving.run_simple` for more + information. """ if magic_app_id: if os.path.isfile(app_id) or os.sep in app_id or \ @@ -152,7 +157,7 @@ def run_application(app_id, host='127.0.0.1', port=5000, debug=None, app = DispatchingApp(app_id, debug, use_eager_loading) run_simple(host, port, app, use_reloader=use_reloader, - use_debugger=use_debugger) + use_debugger=use_debugger, **options) def main(as_module=False): @@ -195,6 +200,13 @@ def main(as_module=False): parser.add_option('--without-eager-loading', action='store_false', dest='with_eager_loading', help='Disable the eager-loading.') + parser.add_option('--with-threads', action='store_true', + dest='with_threads', + help='Enable multi-threading to handle multiple ' + 'requests concurrently.') + parser.add_option('--without-threads', action='store_false', + dest='with_threads', + help='Disables multi-threading. (default)') opts, args = parser.parse_args() if len(args) != 1: parser.error('Expected exactly one argument which is the import ' @@ -213,7 +225,8 @@ def main(as_module=False): run_application(args[0], opts.host, opts.port, debug=opts.debug, use_reloader=opts.with_reloader, use_debugger=opts.with_debugger, - use_eager_loading=opts.with_eager_loading) + use_eager_loading=opts.with_eager_loading, + threaded=opts.with_threads) if __name__ == '__main__': From 8a46eec4f89ccd93d1fab231a5cbccbae0352e57 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 17:51:52 +0200 Subject: [PATCH 0150/2502] Fixed a reference in the docs. --- docs/patterns/errorpages.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/patterns/errorpages.rst b/docs/patterns/errorpages.rst index ddf73c93..79f94a48 100644 --- a/docs/patterns/errorpages.rst +++ b/docs/patterns/errorpages.rst @@ -1,3 +1,5 @@ +.. _errorpages: + Custom Error Pages ================== From 1b06c8a41119f8c93078e85f6474cb85d7e30b43 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 17:52:04 +0200 Subject: [PATCH 0151/2502] Added __main__ module --- flask/__main__.py | 15 +++++++++++++++ flask/run.py | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 flask/__main__.py diff --git a/flask/__main__.py b/flask/__main__.py new file mode 100644 index 00000000..e3c3bd34 --- /dev/null +++ b/flask/__main__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +""" + flask.__main__ + ~~~~~~~~~~~~~~ + + Alias for flask.run for the command line. + + :copyright: (c) 2014 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + + +if __name__ == '__main__': + from run import main + main(as_module=True) diff --git a/flask/run.py b/flask/run.py index 89344616..9d86df02 100644 --- a/flask/run.py +++ b/flask/run.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +""" + flask.run + ~~~~~~~~~ + + A simple command line application to run flask apps. + + :copyright: (c) 2014 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + import os import sys from threading import Lock @@ -164,7 +175,10 @@ def main(as_module=False): this_module = __package__ + '.run' if as_module: - name = 'python -m ' + this_module + if sys.version_info >= (2, 7): + name = 'python -m ' + this_module.rsplit('.', 1)[0] + else: + name = 'python -m ' + this_module else: name = 'flask-run' From e21afed0bba82668c6e4f5f433d0ccfcf54b3577 Mon Sep 17 00:00:00 2001 From: Zak Johnson Date: Mon, 21 Apr 2014 15:37:23 -0700 Subject: [PATCH 0152/2502] Fix typos in docstrings and comments in run.py --- flask/run.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flask/run.py b/flask/run.py index 9d86df02..d16b3e0a 100644 --- a/flask/run.py +++ b/flask/run.py @@ -30,7 +30,7 @@ def find_best_app(module): if app is not None and callable(app): return app - # Otherwise find the first object named Flask + # Otherwise find exactly one Flask instance, or fail. matches = [] for key, value in iteritems(module.__dict__): if isinstance(value, Flask): @@ -52,7 +52,7 @@ def find_best_app(module): def prepare_exec_for_file(filename): module = [] - # Chop off file extensions or package markers + # Chop off file extensions or package markers. if filename.endswith('.py'): filename = filename[:-3] elif os.path.split(filename)[1] == '__init__.py': @@ -93,10 +93,10 @@ def locate_app(app_id, debug=None): class DispatchingApp(object): - """Special applicationt that dispatches to a flask application which + """Special application that dispatches to a flask application which is imported by name on first request. This is safer than importing - the application upfront because it means that we can forward all - errors for import problems into the browser as error. + the application up front because it means that we can forward all + errors for import problems into the browser as errors. """ def __init__(self, app_id, debug=None, use_eager_loading=False): @@ -127,7 +127,7 @@ def run_application(app_id, host='127.0.0.1', port=5000, debug=None, use_eager_loading=None, magic_app_id=True, **options): """Useful function to start a Werkzeug server for an application that - is known by it's import name. By default the app ID can also be a + is known by its import name. By default the app ID can also be a full file name in which case Flask attempts to reconstruct the import name from it and do the right thing. @@ -159,7 +159,7 @@ def run_application(app_id, host='127.0.0.1', port=5000, debug=None, if use_eager_loading is None: use_eager_loading = not use_reloader - # Extra startup messages. This depends a but on Werkzeug internals to + # Extra startup messages. This depends a bit on Werkzeug internals to # not double execute when the reloader kicks in. if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': print ' * Serving Flask app "%s"' % app_id From 52fa195d459a70f8ef73e954b20a9512b2f5001b Mon Sep 17 00:00:00 2001 From: "Carlos E. Garcia" Date: Wed, 23 Apr 2014 10:46:38 -0400 Subject: [PATCH 0153/2502] few mispelling errors --- docs/tutorial/dbcon.rst | 2 +- flask/_compat.py | 2 +- flask/helpers.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst index fb5a0c4a..0e3f7de5 100644 --- a/docs/tutorial/dbcon.rst +++ b/docs/tutorial/dbcon.rst @@ -53,7 +53,7 @@ every time the app context tears down. So what does this mean? Essentially the app context is created before the request comes in and is destroyed (teared down) whenever the request finishes. A teardown can happen because of two reasons: either everything went well (the error -parameter will be `None`) or an exception happend in which case the error +parameter will be `None`) or an exception happened in which case the error is passed to the teardown function. Curious about what these contexts mean? Have a look at the diff --git a/flask/_compat.py b/flask/_compat.py index 86a87832..d4ec9839 100644 --- a/flask/_compat.py +++ b/flask/_compat.py @@ -77,7 +77,7 @@ def with_metaclass(meta, *bases): # breaks the __exit__ function in a very peculiar way. This is currently # true for pypy 2.2.1 for instance. The second level of exception blocks # is necessary because pypy seems to forget to check if an exception -# happend until the next bytecode instruction? +# happened until the next bytecode instruction? BROKEN_PYPY_CTXMGR_EXIT = False if hasattr(sys, 'pypy_version_info'): class _Mgr(object): diff --git a/flask/helpers.py b/flask/helpers.py index 920c4c87..77148708 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -662,7 +662,7 @@ def get_root_path(import_name): 'module came from an import hook that does ' 'not provide file name information or because ' 'it\'s a namespace package. In this case ' - 'the root path needs to be explictly ' + 'the root path needs to be explicitly ' 'provided.' % import_name) # filepath is import_name.py for a module, or __init__.py for a package. From 16308bf6754facf0fec8b8e8644a0a4940cc0ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:13:19 +0200 Subject: [PATCH 0154/2502] Check links in the documentation with tox --- tox.ini | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index b09458d9..ceb42b6d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,11 @@ [tox] -envlist = py26, py27, pypy, py33 +envlist = docs, py26, py27, pypy, py33 [testenv] deps = blinker commands = python run-tests.py [] + + +[testenv:docs] +deps = sphinx +commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/linkcheck From 20edb3189c82c5f5fddc1792930bcf5391fb6f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:20:12 +0200 Subject: [PATCH 0155/2502] Fix Pocoo documentation links --- docs/index.rst | 6 +++--- docs/installation.rst | 2 +- docs/quickstart.rst | 4 ++-- docs/templating.rst | 2 +- docs/tutorial/templates.rst | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 43702409..617104ee 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,11 +21,11 @@ engine and the `Werkzeug`_ WSGI toolkit. These libraries are not documented here. If you want to dive into their documentation, check out the following links: -- `Jinja2 Documentation `_ -- `Werkzeug Documentation `_ +- `Jinja2 Documentation `_ +- `Werkzeug Documentation `_ -.. _Jinja2: http://jinja.pocoo.org/2/ +.. _Jinja2: http://jinja.pocoo.org/ .. _Werkzeug: http://werkzeug.pocoo.org/ .. include:: contents.rst.inc diff --git a/docs/installation.rst b/docs/installation.rst index 78f192fd..163782f2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -4,7 +4,7 @@ Installation ============ Flask depends on some external libraries, like `Werkzeug -`_ and `Jinja2 `_. +`_ 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. Jinja2 renders templates. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index a4494f43..05e27e74 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -347,7 +347,7 @@ Rendering Templates Generating HTML from within Python is not fun, and actually pretty cumbersome because you have to do the HTML escaping on your own to keep the application secure. Because of that Flask configures the `Jinja2 -`_ template engine for you automatically. +`_ template engine for you automatically. To render a template you can use the :func:`~flask.render_template` method. All you have to do is provide the name of the template and the @@ -380,7 +380,7 @@ package it's actually inside your package: For templates you can use the full power of Jinja2 templates. Head over to the official `Jinja2 Template Documentation -`_ for more information. +`_ for more information. Here is an example template: diff --git a/docs/templating.rst b/docs/templating.rst index 4e432333..bf672672 100644 --- a/docs/templating.rst +++ b/docs/templating.rst @@ -9,7 +9,7 @@ An extension can depend on Jinja2 being present. This section only gives a very quick introduction into how Jinja2 is integrated into Flask. If you want information on the template engine's syntax itself, head over to the official `Jinja2 Template -Documentation `_ for +Documentation `_ for more information. Jinja Setup diff --git a/docs/tutorial/templates.rst b/docs/tutorial/templates.rst index 5ec5584d..fd3abe68 100644 --- a/docs/tutorial/templates.rst +++ b/docs/tutorial/templates.rst @@ -16,7 +16,7 @@ the layout of the website in all pages. Put the following templates into the `templates` folder: -.. _Jinja2: http://jinja.pocoo.org/2/documentation/templates +.. _Jinja2: http://jinja.pocoo.org/docs/templates layout.html ----------- From b5e78425be985eeec4c91ea8277368a9fae9411a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:24:07 +0200 Subject: [PATCH 0156/2502] Link to https version of python docs To which the http version permanently redirects --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 16c841f4..524ee555 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -244,7 +244,7 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf'] #epub_tocdepth = 3 intersphinx_mapping = { - 'http://docs.python.org/dev': None, + 'https://docs.python.org/dev': None, 'http://werkzeug.pocoo.org/docs/': None, 'http://www.sqlalchemy.org/docs/': None, 'http://wtforms.simplecodes.com/docs/0.5/': None, From 954eeb8d1784029cb09da04eef6470036ce5dc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:26:12 +0200 Subject: [PATCH 0157/2502] Fix Jinja link --- docs/tutorial/folders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/folders.rst b/docs/tutorial/folders.rst index 4bf47cd7..e1ff229d 100644 --- a/docs/tutorial/folders.rst +++ b/docs/tutorial/folders.rst @@ -20,4 +20,4 @@ templates you create later in the tutorial will go in this directory. Continue with :ref:`tutorial-schema`. -.. _Jinja2: http://jinja.pocoo.org/2/ +.. _Jinja2: http://jinja.pocoo.org/ From 34871a286efac32b206d9175ec546df7f47dcb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:30:28 +0200 Subject: [PATCH 0158/2502] Switch pypi links to https To which the http version redirects permanently --- CHANGES | 2 +- docs/api.rst | 2 +- docs/deploying/mod_wsgi.rst | 2 +- docs/patterns/caching.rst | 2 +- docs/patterns/distribute.rst | 4 ++-- docs/patterns/sqlalchemy.rst | 2 +- docs/patterns/wtforms.rst | 2 +- docs/signals.rst | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 80c4b27d..86f588cf 100644 --- a/CHANGES +++ b/CHANGES @@ -400,7 +400,7 @@ Released on July 27th 2010, codename Whisky prefix. This makes it possible to bind a whole module to a configurable subdomain. -.. _blinker: http://pypi.python.org/pypi/blinker +.. _blinker: https://pypi.python.org/pypi/blinker Version 0.5.2 ------------- diff --git a/docs/api.rst b/docs/api.rst index c87055d1..6c9f7414 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -597,7 +597,7 @@ Signals do nothing but will fail with a :exc:`RuntimeError` for all other operations, including connecting. -.. _blinker: http://pypi.python.org/pypi/blinker +.. _blinker: https://pypi.python.org/pypi/blinker Class-Based Views ----------------- diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index 8fd2c0bb..baac5a1b 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -109,7 +109,7 @@ For more information consult the `mod_wsgi wiki`_. .. _mod_wsgi: http://code.google.com/p/modwsgi/ .. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide -.. _virtual python: http://pypi.python.org/pypi/virtualenv +.. _virtual python: https://pypi.python.org/pypi/virtualenv .. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/ Troubleshooting diff --git a/docs/patterns/caching.rst b/docs/patterns/caching.rst index 5817aa29..a0633cf9 100644 --- a/docs/patterns/caching.rst +++ b/docs/patterns/caching.rst @@ -27,7 +27,7 @@ cache that keeps the item stored in the memory of the Python interpreter:: cache = SimpleCache() If you want to use memcached, make sure to have one of the memcache modules -supported (you get them from `PyPI `_) and a +supported (you get them from `PyPI `_) and a memcached server running somewhere. This is how you connect to such an memcached server then:: diff --git a/docs/patterns/distribute.rst b/docs/patterns/distribute.rst index b6f6a5ef..aa53e685 100644 --- a/docs/patterns/distribute.rst +++ b/docs/patterns/distribute.rst @@ -161,6 +161,6 @@ folder instead of copying the data over. You can then continue to work on the code without having to run `install` again after each change. -.. _distribute: http://pypi.python.org/pypi/distribute -.. _pip: http://pypi.python.org/pypi/pip +.. _distribute: https://pypi.python.org/pypi/distribute +.. _pip: https://pypi.python.org/pypi/pip .. _distribute_setup.py: http://python-distribute.org/distribute_setup.py diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index 3c4d9ce9..dd0eee91 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -20,7 +20,7 @@ there is a Flask extension that handles that for you. This is recommended if you want to get started quickly. You can download `Flask-SQLAlchemy`_ from `PyPI -`_. +`_. .. _Flask-SQLAlchemy: http://packages.python.org/Flask-SQLAlchemy/ diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst index 1bf46637..005efebb 100644 --- a/docs/patterns/wtforms.rst +++ b/docs/patterns/wtforms.rst @@ -17,7 +17,7 @@ forms. The `Flask-WTF`_ extension expands on this pattern and adds a few handful little helpers that make working with forms and Flask more fun. You can get it from `PyPI - `_. + `_. .. _Flask-WTF: http://packages.python.org/Flask-WTF/ diff --git a/docs/signals.rst b/docs/signals.rst index c9df1edf..46342033 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -349,4 +349,4 @@ The following signals exist in Flask: .. versionadded:: 0.10 -.. _blinker: http://pypi.python.org/pypi/blinker +.. _blinker: https://pypi.python.org/pypi/blinker From e89c867d62f351bad4ca4bb6bc096695e529d45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:35:37 +0200 Subject: [PATCH 0159/2502] Switch packages.python.org to pythonhosted.org --- docs/extensiondev.rst | 2 +- docs/patterns/fileuploads.rst | 2 +- docs/patterns/sqlalchemy.rst | 2 +- docs/patterns/wtforms.rst | 4 ++-- docs/upgrading.rst | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 09bf2d2c..4e0e45d8 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -415,6 +415,6 @@ instead of ``flask_foo`` or ``flaskext_foo`` so that extensions can transition to the new package name without affecting users. -.. _OAuth extension: http://packages.python.org/Flask-OAuth/ +.. _OAuth extension: http://pythonhosted.org/Flask-OAuth/ .. _mailinglist: http://flask.pocoo.org/mailinglist/ .. _IRC channel: http://flask.pocoo.org/community/irc/ diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index eef50a9a..74cdd98b 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -178,4 +178,4 @@ applications dealing with uploads, there is a Flask extension called `Flask-Uploads`_ that implements a full fledged upload mechanism with white and blacklisting of extensions and more. -.. _Flask-Uploads: http://packages.python.org/Flask-Uploads/ +.. _Flask-Uploads: http://pythonhosted.org/Flask-Uploads/ diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index dd0eee91..3b517a6d 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -22,7 +22,7 @@ if you want to get started quickly. You can download `Flask-SQLAlchemy`_ from `PyPI `_. -.. _Flask-SQLAlchemy: http://packages.python.org/Flask-SQLAlchemy/ +.. _Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/ Declarative diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst index 005efebb..8017aea5 100644 --- a/docs/patterns/wtforms.rst +++ b/docs/patterns/wtforms.rst @@ -8,7 +8,7 @@ will handle here. If you find yourself in the situation of having many forms, you might want to give it a try. When you are working with WTForms you have to define your forms as classes -first. I recommend breaking up the application into multiple modules +first. I recommend breaking up the application into multiple modules (:ref:`larger-applications`) for that and adding a separate module for the forms. @@ -19,7 +19,7 @@ forms. fun. You can get it from `PyPI `_. -.. _Flask-WTF: http://packages.python.org/Flask-WTF/ +.. _Flask-WTF: http://pythonhosted.org/Flask-WTF/ The Forms --------- diff --git a/docs/upgrading.rst b/docs/upgrading.rst index ebea0101..ccfa82ad 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -43,7 +43,7 @@ when there is no request context yet but an application context. The old ``flask.Flask.request_globals_class`` attribute was renamed to :attr:`flask.Flask.app_ctx_globals_class`. -.. _Flask-OldSessions: http://packages.python.org/Flask-OldSessions/ +.. _Flask-OldSessions: http://pythonhosted.org/Flask-OldSessions/ Version 0.9 ----------- From 52def324b882dc99e0254abcefd1ba608b29a1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:36:36 +0200 Subject: [PATCH 0160/2502] Use https instead of http for docs.python.org --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 05e27e74..930a2f0c 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -818,7 +818,7 @@ Here are some example log calls:: The attached :attr:`~flask.Flask.logger` is a standard logging :class:`~logging.Logger`, so head over to the official `logging -documentation `_ for more +documentation `_ for more information. Hooking in WSGI Middlewares From 51228ad0d17eac9217cf0780bb2490a1b3a24a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:37:19 +0200 Subject: [PATCH 0161/2502] Use https for devcenter.heroku.com --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 930a2f0c..47d85ad5 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -841,7 +841,7 @@ Ready to deploy your new Flask app? To wrap up the quickstart, you can immediately deploy to a hosted platform, all of which offer a free plan for small projects: -- `Deploying Flask on Heroku `_ +- `Deploying Flask on Heroku `_ - `Deploying WSGI on dotCloud `_ with `Flask-specific notes `_ From 13293b499faccb30e63a2477cfd780d2297a6fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:38:20 +0200 Subject: [PATCH 0162/2502] Use libevent.org --- docs/deploying/wsgi-standalone.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deploying/wsgi-standalone.rst b/docs/deploying/wsgi-standalone.rst index c8d4f20e..11d07831 100644 --- a/docs/deploying/wsgi-standalone.rst +++ b/docs/deploying/wsgi-standalone.rst @@ -64,7 +64,7 @@ event loop:: .. _Gevent: http://www.gevent.org/ .. _greenlet: http://greenlet.readthedocs.org/en/latest/ -.. _libevent: http://monkey.org/~provos/libevent/ +.. _libevent: http://libevent.org/ Twisted Web ----------- From e991bef57482152639e7b797ab98f2710ab9c4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:39:22 +0200 Subject: [PATCH 0163/2502] Switch diveintohtml5.org to diveintohtml5.info --- docs/htmlfaq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/htmlfaq.rst b/docs/htmlfaq.rst index b16f4cd5..434bb656 100644 --- a/docs/htmlfaq.rst +++ b/docs/htmlfaq.rst @@ -186,7 +186,7 @@ Many other features have been added, as well. A good guide to new features in HTML5 is Mark Pilgrim's soon-to-be-published book, `Dive Into HTML5`_. Not all of them are supported in browsers yet, however, so use caution. -.. _Dive Into HTML5: http://www.diveintohtml5.org/ +.. _Dive Into HTML5: http://www.diveintohtml5.info/ What should be used? -------------------- From 033b0931aaf32f1f34045270d89fc64b29835329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:45:20 +0200 Subject: [PATCH 0164/2502] Run tests with Python 3.4 with tox --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ceb42b6d..24155a9a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = docs, py26, py27, pypy, py33 +envlist = docs, py26, py27, pypy, py33, py34 [testenv] deps = blinker From 0ac492a0acda40208e6c613644fe9e779dc3d937 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:26:14 +0200 Subject: [PATCH 0165/2502] Depend on click now --- setup.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 0fbf7e8d..196eca4c 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ Links from __future__ import print_function from setuptools import Command, setup + class run_audit(Command): """Audits source code using PyFlakes for following issues: - Names which are used but not defined or used before they are defined. @@ -56,7 +57,8 @@ class run_audit(Command): pass def run(self): - import os, sys + import os + import sys try: import pyflakes.scripts.pyflakes as flakes except ImportError: @@ -69,7 +71,7 @@ class run_audit(Command): for dir in dirs: for root, _, files in os.walk(dir): for file in files: - if file != '__init__.py' and file.endswith('.py') : + if file != '__init__.py' and file.endswith('.py'): warns += flakes.checkPath(os.path.join(root, file)) if warns > 0: print("Audit finished with total %d warnings." % warns) @@ -93,7 +95,8 @@ setup( install_requires=[ 'Werkzeug>=0.7', 'Jinja2>=2.4', - 'itsdangerous>=0.21' + 'itsdangerous>=0.21', + 'click', ], classifiers=[ 'Development Status :: 4 - Beta', @@ -108,7 +111,7 @@ setup( ], entry_points=''' [console_scripts] - flask-run=flask.run:main + flask=flask.cli:main ''', cmdclass={'audit': run_audit}, test_suite='flask.testsuite.suite' From 3bdb90f06b9d3167320180d4a5055dcd949bf72f Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:26:23 +0200 Subject: [PATCH 0166/2502] Added click support to Flask --- flask/__main__.py | 2 +- flask/app.py | 47 ++++++- flask/cli.py | 347 ++++++++++++++++++++++++++++++++++++++++++++++ flask/run.py | 247 --------------------------------- 4 files changed, 392 insertions(+), 251 deletions(-) create mode 100644 flask/cli.py delete mode 100644 flask/run.py diff --git a/flask/__main__.py b/flask/__main__.py index e3c3bd34..cbcdebf1 100644 --- a/flask/__main__.py +++ b/flask/__main__.py @@ -11,5 +11,5 @@ if __name__ == '__main__': - from run import main + from cli import main main(as_module=True) diff --git a/flask/app.py b/flask/app.py index bb5cbbd7..3e421f3d 100644 --- a/flask/app.py +++ b/flask/app.py @@ -34,6 +34,7 @@ from .templating import DispatchingJinjaLoader, Environment, \ _default_template_ctx_processor from .signals import request_started, request_finished, got_request_exception, \ request_tearing_down, appcontext_tearing_down +from .cli import make_default_cli from ._compat import reraise, string_types, text_type, integer_types # a lock used for logger initialization @@ -476,6 +477,12 @@ class Flask(_PackageBoundObject): None: [_default_template_ctx_processor] } + #: A list of shell context processor functions that should be run + #: when a shell context is created. + #: + #: .. versionadded:: 1.0 + self.shell_context_processors = [] + #: all the attached blueprints in a dictionary by name. Blueprints #: can be attached multiple times so this dictionary does not tell #: you how often they got attached. @@ -531,6 +538,14 @@ class Flask(_PackageBoundObject): endpoint='static', view_func=self.send_static_file) + #: The click command line context for this application. Commands + #: registered here show up in the ``flask`` command once the + #: application has been discovered. The default commands are + #: provided by Flask itself and can be overridden. + #: + #: This is an instance of a :class:`click.Group` object. + self.cli = make_default_cli(self) + def _get_error_handlers(self): from warnings import warn warn(DeprecationWarning('error_handlers is deprecated, use the ' @@ -748,6 +763,18 @@ class Flask(_PackageBoundObject): # existing views. context.update(orig_ctx) + def make_shell_context(self): + """Returns the shell context for an interactive shell for this + application. This runs all the registered shell context + processors. + + .. versionadded:: 1.0 + """ + rv = {'app': self, 'g': g} + for processor in self.shell_context_processors: + rv.update(processor()) + return rv + def run(self, host=None, port=None, debug=None, **options): """Runs the application on a local development server. If the :attr:`debug` flag is set the server will automatically reload @@ -758,6 +785,11 @@ class Flask(_PackageBoundObject): ``use_evalex=False`` as parameter. This will keep the debugger's traceback screen active, but disable code execution. + It is not recommended to use this function for development with + automatic reloading as this is badly supported. Instead you should + be using the ``flask`` command line script's ``runserver`` + support. + .. admonition:: Keep in Mind Flask will suppress any server error with a generic error page @@ -1091,7 +1123,7 @@ class Flask(_PackageBoundObject): 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 @@ -1325,6 +1357,15 @@ class Flask(_PackageBoundObject): self.template_context_processors[None].append(f) return f + @setupmethod + def shell_context_processor(self, f): + """Registers a shell context processor function. + + .. versionadded:: 1.0 + """ + self.shell_context_processors.append(f) + return f + @setupmethod def url_value_preprocessor(self, f): """Registers a function as URL value preprocessor for all view @@ -1609,7 +1650,8 @@ class Flask(_PackageBoundObject): # some extra logic involved when creating these objects with # specific values (like default content type selection). if isinstance(rv, (text_type, bytes, bytearray)): - rv = self.response_class(rv, headers=headers, status=status_or_headers) + rv = self.response_class(rv, headers=headers, + status=status_or_headers) headers = status_or_headers = None else: rv = self.response_class.force_type(rv, request.environ) @@ -1624,7 +1666,6 @@ class Flask(_PackageBoundObject): return rv - def create_url_adapter(self, request): """Creates a URL adapter for the given request. The URL adapter is created at a point where the request context is not yet set up diff --git a/flask/cli.py b/flask/cli.py new file mode 100644 index 00000000..7e982878 --- /dev/null +++ b/flask/cli.py @@ -0,0 +1,347 @@ +# -*- coding: utf-8 -*- +""" + flask.run + ~~~~~~~~~ + + A simple command line application to run flask apps. + + :copyright: (c) 2014 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + +import os +import sys +from threading import Lock +from contextlib import contextmanager + +import click + +from ._compat import iteritems + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(module): + """Given a module instance this tries to find the best possible + application in the module or raises an exception. + """ + from . import Flask + + # Search for the most common names first. + for attr_name in 'app', 'application': + app = getattr(module, attr_name, None) + if app is not None and isinstance(app, Flask): + return app + + # Otherwise find the only object that is a Flask instance. + matches = [v for k, v in iteritems(module.__dict__) + if isinstance(v, Flask)] + + if matches: + if len(matches) > 1: + raise NoAppException('More than one possible Flask application ' + 'found in module "%s", none of which are called ' + '"app". Be explicit!' % module.__name__) + return matches[0] + + raise NoAppException('Failed to find application in module "%s". Are ' + 'you sure it contains a Flask application? Maybe ' + 'you wrapped it in a WSGI middleware or you are ' + 'using a factory function.' % module.__name__) + + +def prepare_exec_for_file(filename): + module = [] + + # Chop off file extensions or package markers + if filename.endswith('.py'): + filename = filename[:-3] + elif os.path.split(filename)[1] == '__init__.py': + filename = os.path.dirname(filename) + filename = os.path.realpath(filename) + + dirpath = filename + while 1: + dirpath, extra = os.path.split(dirpath) + module.append(extra) + if not os.path.isfile(os.path.join(dirpath, '__init__.py')): + break + + sys.path.insert(0, dirpath) + return '.'.join(module[::-1]) + + +def locate_app(app_id, debug=None): + """Attempts to locate the application.""" + if ':' in app_id: + module, app_obj = app_id.split(':', 1) + else: + module = app_id + app_obj = None + + __import__(module) + mod = sys.modules[module] + if app_obj is None: + app = find_best_app(mod) + else: + app = getattr(mod, app_obj, None) + if app is None: + raise RuntimeError('Failed to find application in module "%s"' + % module) + if debug is not None: + app.debug = debug + return app + + +class DispatchingApp(object): + """Special applicationt that dispatches to a flask application which + is imported by name on first request. This is safer than importing + the application upfront because it means that we can forward all + errors for import problems into the browser as error. + """ + + def __init__(self, app_id, debug=None, use_eager_loading=False): + self.app_id = app_id + self.app = None + self.debug = debug + self._lock = Lock() + if use_eager_loading: + self._load_unlocked() + + def _load_unlocked(self): + self.app = rv = locate_app(self.app_id, self.debug) + return rv + + def __call__(self, environ, start_response): + if self.app is not None: + return self.app(environ, start_response) + with self._lock: + if self.app is not None: + rv = self.app + else: + rv = self._load_unlocked() + return rv(environ, start_response) + + +class ScriptInfo(object): + """Help object to deal with Flask applications. This is usually not + necessary to interface with as it's used internally in the dispatching + to click. + """ + + def __init__(self): + self.app_import_path = None + self.debug = None + self._loaded_app = None + + def get_app_import_path(self): + """Return the actual application import path or fails if it is + not yet set. + """ + if self.app_import_path is not None: + return self.app_import_path + raise NoAppException('Could not locate application. ' + 'You did not provide FLASK_APP or the ' + '--app parameter.') + + def load_app(self): + """Loads the app (if not yet loaded) and returns it.""" + if self._loaded_app is not None: + return self._loaded_app + rv = locate_app(self.get_app_import_path(), self.debug) + self._loaded_app = rv + return rv + + @contextmanager + def conditional_context(self, with_context=True): + """Creates an application context or not, depending on the + given parameter but always works as context manager. + """ + if with_context: + with self.load_app().app_context() as ctx: + yield ctx + else: + yield None + + +pass_script_info = click.make_pass_decorator(ScriptInfo) + + +def without_appcontext(f): + """Marks a click callback so that it does not get a app context + created. This only works for commands directly registered to + the toplevel system. This really is only useful for very + special commands like the runserver one. + """ + f.__flask_without_appcontext__ = True + return f + + +class FlaskClickGroup(click.Group): + """Special subclass of the a regular click group that supports + loading more commands from the configured Flask app. + """ + + def __init__(self, help=None): + def set_app_id(ctx, value): + if value is not None: + if os.path.isfile(value) or os.sep in value or \ + os.altsep is not None and os.altsep in value: + value = prepare_exec_for_file(value) + ctx.obj.app_import_path = value + def set_debug(ctx, value): + ctx.obj.debug = value + + click.Group.__init__(self, help=help, params=[ + click.Option(['-a', '--app'], + help='The application to run', + callback=set_app_id, is_eager=True), + click.Option(['--debug/--no-debug'], + help='Enable or disable debug mode.', + default=None, callback=set_debug) + ]) + + def get_command(self, ctx, name): + info = ctx.find_object(ScriptInfo) + # Find the command in the application first, if we can find it. + # If the app is not available, we just ignore this silently. + try: + rv = info.load_app().cli.get_command(ctx, name) + if rv is not None: + return rv + except NoAppException: + pass + return click.Group.get_command(self, ctx, name) + + def list_commands(self, ctx): + # The commands available is the list of both the application (if + # available) plus the builtin commands. + rv = set(click.Group.list_commands(self, ctx)) + info = ctx.find_object(ScriptInfo) + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except NoAppException: + pass + return sorted(rv) + + def invoke_subcommand(self, ctx, cmd, cmd_name, args): + with_context = cmd.callback is None or \ + not getattr(cmd.callback, '__flask_without_appcontext__', False) + + with ctx.find_object(ScriptInfo).conditional_context(with_context): + return click.Group.invoke_subcommand( + self, ctx, cmd, cmd_name, args) + + +cli = FlaskClickGroup(help='''\ +This shell command acts as general utility script for Flask applications. + +It loads the application configured (either through the FLASK_APP environment +variable or the --app parameter) and then provides commands either provided +by the application or Flask itself. + +The most useful commands are the "run" and "shell" command. + +Example usage: + + flask --app=hello --debug run +''') + + +@cli.command('run', short_help='Runs a development server.') +@click.option('--host', '-h', default='127.0.0.1', + help='The interface to bind to.') +@click.option('--port', '-p', default=5000, + help='The port to bind to.') +@click.option('--reload/--no-reload', default=None, + help='Enable or disable the reloader. By default the reloader ' + 'is active is debug is enabled.') +@click.option('--debugger/--no-debugger', default=None, + help='Enable or disable the debugger. By default the debugger ' + 'is active if debug is enabled.') +@click.option('--eager-loading/--lazy-loader', default=None, + help='Enable or disable eager loading. By default eager ' + 'loading is enabled if the reloader is disabled.') +@click.option('--with-threads/--without-threads', default=False, + help='Enable or disable multithreading.') +@without_appcontext +@pass_script_info +def run_command(info, host, port, reload, debugger, eager_loading, + with_threads): + """Runs a local development server for the Flask application.""" + from werkzeug.serving import run_simple + app_id = info.get_app_import_path() + if reload is None: + reload = info.debug + if debugger is None: + debugger = info.debug + if eager_loading is None: + eager_loading = not reload + + # Extra startup messages. This depends a but on Werkzeug internals to + # not double execute when the reloader kicks in. + if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': + print(' * Serving Flask app "%s"' % app_id) + if info.debug is not None: + print(' * Forcing debug %s' % (info.debug and 'on' or 'off')) + + app = DispatchingApp(app_id, info.debug, eager_loading) + run_simple(host, port, app, use_reloader=reload, + use_debugger=debugger, threaded=with_threads) + + +@cli.command('shell', short_help='Runs a shell in the app context.') +def shell_command(): + """Runs an interactive Python shell in the context of a given + Flask application. The application will populate the default + namespace of this shell according to it's configuration. + + This is useful for executing small snippets of management code + without having to manually configuring the application. + """ + import code + from flask.globals import _app_ctx_stack + app = _app_ctx_stack.top.app + banner = 'Python %s on %s\nApp: %s%s\nInstance: %s' % ( + sys.version, + sys.platform, + app.import_name, + app.debug and ' [debug]' or '', + app.instance_path, + ) + code.interact(banner=banner, local=app.make_shell_context()) + + +def make_default_cli(app): + """Creates the default click object for the app itself. Currently + there are no default commands registered because all builtin commands + are registered on the actual cmd object here. + """ + return click.Group() + + +def main(as_module=False): + this_module = __package__ + '.cli' + args = sys.argv[1:] + + if as_module: + if sys.version_info >= (2, 7): + name = 'python -m ' + this_module.rsplit('.', 1)[0] + else: + name = 'python -m ' + this_module + + # This module is always executed as "python -m flask.run" and as such + # we need to ensure that we restore the actual command line so that + # the reloader can properly operate. + sys.argv = ['-m', this_module] + sys.argv[1:] + else: + name = 'flask' + + cli.main(args=args, prog_name=name, obj=ScriptInfo(), + auto_envvar_prefix='FLASK') + + +if __name__ == '__main__': + main(as_module=True) diff --git a/flask/run.py b/flask/run.py deleted file mode 100644 index 9d86df02..00000000 --- a/flask/run.py +++ /dev/null @@ -1,247 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.run - ~~~~~~~~~ - - A simple command line application to run flask apps. - - :copyright: (c) 2014 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import os -import sys -from threading import Lock -from optparse import OptionParser - -from werkzeug.serving import run_simple - -from ._compat import iteritems - - -def find_best_app(module): - """Given a module instance this tries to find the best possible application - in the module or raises a RuntimeError. - """ - from flask import Flask - - # The app name wins, even if it's not a flask object. - app = getattr(module, 'app', None) - if app is not None and callable(app): - return app - - # Otherwise find the first object named Flask - matches = [] - for key, value in iteritems(module.__dict__): - if isinstance(value, Flask): - matches.append(value) - - if matches: - if len(matches) > 1: - raise RuntimeError('More than one possible Flask application ' - 'found in module "%s", none of which are called ' - '"app". Be explicit!' % module) - return matches[0] - - raise RuntimeError('Failed to find application in module "%s". Are ' - 'you sure it contains a Flask application? Maybe ' - 'you wrapped it in a WSGI middleware or you are ' - 'using a factory function.' % module) - - -def prepare_exec_for_file(filename): - module = [] - - # Chop off file extensions or package markers - if filename.endswith('.py'): - filename = filename[:-3] - elif os.path.split(filename)[1] == '__init__.py': - filename = os.path.dirname(filename) - filename = os.path.realpath(filename) - - dirpath = filename - while 1: - dirpath, extra = os.path.split(dirpath) - module.append(extra) - if not os.path.isfile(os.path.join(dirpath, '__init__.py')): - break - - sys.path.insert(0, dirpath) - return '.'.join(module[::-1]) - - -def locate_app(app_id, debug=None): - """Attempts to locate the application.""" - if ':' in app_id: - module, app_obj = app_id.split(':', 1) - else: - module = app_id - app_obj = None - - __import__(module) - mod = sys.modules[module] - if app_obj is None: - app = find_best_app(mod) - else: - app = getattr(mod, app_obj, None) - if app is None: - raise RuntimeError('Failed to find application in module "%s"' - % module) - if debug is not None: - app.debug = debug - return app - - -class DispatchingApp(object): - """Special applicationt that dispatches to a flask application which - is imported by name on first request. This is safer than importing - the application upfront because it means that we can forward all - errors for import problems into the browser as error. - """ - - def __init__(self, app_id, debug=None, use_eager_loading=False): - self.app_id = app_id - self.app = None - self.debug = debug - self._lock = Lock() - if use_eager_loading: - self._load_unlocked() - - def _load_unlocked(self): - self.app = rv = locate_app(self.app_id, self.debug) - return rv - - def __call__(self, environ, start_response): - if self.app is not None: - return self.app(environ, start_response) - with self._lock: - if self.app is not None: - rv = self.app - else: - rv = self._load_unlocked() - return rv(environ, start_response) - - -def run_application(app_id, host='127.0.0.1', port=5000, debug=None, - use_reloader=False, use_debugger=False, - use_eager_loading=None, magic_app_id=True, - **options): - """Useful function to start a Werkzeug server for an application that - is known by it's import name. By default the app ID can also be a - full file name in which case Flask attempts to reconstruct the import - name from it and do the right thing. - - :param app_id: the import name of the application module. If a colon - is provided, everything afterwards is the application - object name. In case the magic app id is enabled this - can also be a filename. - :param host: the host to bind to. - :param port: the port to bind to. - :param debug: if set to something other than None then the application's - debug flag will be set to this. - :param use_reloader: enables or disables the reloader. - :param use_debugger: enables or disables the builtin debugger. - :param use_eager_loading: enables or disables eager loading. This is - normally conditional to the reloader. - :param magic_app_id: if this is enabled then the app id can also be a - filename instead of an import module and Flask - will attempt to reconstruct the import name. - :param options: the options to be forwarded to the underlying - Werkzeug server. See - :func:`werkzeug.serving.run_simple` for more - information. - """ - if magic_app_id: - if os.path.isfile(app_id) or os.sep in app_id or \ - os.altsep is not None and os.altsep in app_id: - app_id = prepare_exec_for_file(app_id) - - if use_eager_loading is None: - use_eager_loading = not use_reloader - - # Extra startup messages. This depends a but on Werkzeug internals to - # not double execute when the reloader kicks in. - if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': - print ' * Serving Flask app "%s"' % app_id - if debug is not None: - print ' * Forcing debug %s' % (debug and 'on' or 'off') - - app = DispatchingApp(app_id, debug, use_eager_loading) - run_simple(host, port, app, use_reloader=use_reloader, - use_debugger=use_debugger, **options) - - -def main(as_module=False): - this_module = __package__ + '.run' - - if as_module: - if sys.version_info >= (2, 7): - name = 'python -m ' + this_module.rsplit('.', 1)[0] - else: - name = 'python -m ' + this_module - else: - name = 'flask-run' - - parser = OptionParser(usage='%prog [options] module', prog=name) - parser.add_option('--debug', action='store_true', - dest='debug', help='Flip debug flag on. If enabled ' - 'this also affects debugger and reloader defaults.') - parser.add_option('--no-debug', action='store_false', - dest='debug', help='Flip debug flag off.') - parser.add_option('--host', default='127.0.0.1', - help='The host to bind on. (defaults to 127.0.0.1)') - parser.add_option('--port', default=5000, - help='The port to bind on. (defaults to 5000)') - parser.add_option('--with-reloader', action='store_true', - dest='with_reloader', - help='Enable the reloader.') - parser.add_option('--without-reloader', action='store_false', - dest='with_reloader', - help='Disable the reloader.') - parser.add_option('--with-debugger', action='store_true', - dest='with_debugger', - help='Enable the debugger.') - parser.add_option('--without-debugger', action='store_false', - dest='with_debugger', - help='Disable the debugger.') - parser.add_option('--with-eager-loading', action='store_true', - dest='with_eager_loading', - help='Force enable the eager-loading. This makes the ' - 'application load immediately but makes development ' - 'flows harder. It\'s not recommended to enable eager ' - 'loading when the reloader is enabled as it can lead ' - 'to unexpected crashes.') - parser.add_option('--without-eager-loading', action='store_false', - dest='with_eager_loading', - help='Disable the eager-loading.') - parser.add_option('--with-threads', action='store_true', - dest='with_threads', - help='Enable multi-threading to handle multiple ' - 'requests concurrently.') - parser.add_option('--without-threads', action='store_false', - dest='with_threads', - help='Disables multi-threading. (default)') - opts, args = parser.parse_args() - if len(args) != 1: - parser.error('Expected exactly one argument which is the import ' - 'name of the application.') - - if opts.with_debugger is None: - opts.with_debugger = opts.debug - if opts.with_reloader is None: - opts.with_reloader = opts.debug - - # This module is always executed as "python -m flask.run" and as such - # we need to ensure that we restore the actual command line so that - # the reloader can properly operate. - sys.argv = ['-m', this_module] + sys.argv[1:] - - run_application(args[0], opts.host, opts.port, debug=opts.debug, - use_reloader=opts.with_reloader, - use_debugger=opts.with_debugger, - use_eager_loading=opts.with_eager_loading, - threaded=opts.with_threads) - - -if __name__ == '__main__': - main(as_module=True) From 7bb3271f1a5556b62efb0ccecf2b1c6bc293ce8b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:27:07 +0200 Subject: [PATCH 0167/2502] Added changelog entry for click --- CHANGES | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 80c4b27d..26050bcc 100644 --- a/CHANGES +++ b/CHANGES @@ -26,9 +26,10 @@ Version 1.0 - Added a workaround for a limitation in Python 3.3's namespace loader. - Added support for explicit root paths when using Python 3.3's namespace packages. -- Added ``flask-run`` and the ``flask.run`` module to start the local - debug server. This is recommended over the old ``flask.run()`` method - as it works faster and more reliable due to a different design. +- Added ``flask`` and the ``flask.cli`` module to start the local + debug server through the click CLI system. This is recommended over the old + ``flask.run()`` method as it works faster and more reliable due to a + different design and also replaces ``Flask-Script``. Version 0.10.2 -------------- From e9d1fc47bf42bb13fce8799ad7727206610a7eb4 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:27:13 +0200 Subject: [PATCH 0168/2502] Updated docs for click support --- docs/contents.rst.inc | 2 + docs/quickstart.rst | 93 ++++++++++++++++++++++++++++++------------- docs/server.rst | 50 +++++++++++++++++++++++ docs/shell.rst | 10 +++++ 4 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 docs/server.rst diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc index 17e79500..8b25e61d 100644 --- a/docs/contents.rst.inc +++ b/docs/contents.rst.inc @@ -23,6 +23,8 @@ instructions for web development with Flask. reqcontext blueprints extensions + cli + server shell patterns/index deploying/index diff --git a/docs/quickstart.rst b/docs/quickstart.rst index a4494f43..bce798f8 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -20,16 +20,19 @@ A minimal Flask application looks something like this:: def hello_world(): return 'Hello World!' - if __name__ == '__main__': - app.run() - Just save it as `hello.py` (or something similar) and run it with your Python interpreter. Make sure to not call your application `flask.py` because this would conflict with Flask itself. -:: +To run the application you can either use the ``flask`` command or +python's ``-m`` switch with Flask:: - $ python hello.py + $ flask -a hello run + * Running on http://127.0.0.1:5000/ + +or alternatively:: + + $ python -m flask -a hello run * Running on http://127.0.0.1:5000/ Now head over to `http://127.0.0.1:5000/ `_, and you @@ -51,10 +54,8 @@ So what did that code do? 4. The function is given a name which is also used to generate URLs for that particular function, and returns the message we want to display in the user's browser. -5. Finally we use the :meth:`~flask.Flask.run` function to run the local server - with our application. The ``if __name__ == '__main__':`` makes sure the - server only runs if the script is executed directly from the Python - interpreter and not used as an imported module. +5. Finally we use the Flask development server to run the local server + with our application. To stop the server, hit control-C. @@ -67,37 +68,73 @@ To stop the server, hit control-C. default because in debugging mode a user of the application can execute arbitrary Python code on your computer. - If you have `debug` disabled or trust the users on your network, you can - make the server publicly available simply by changing the call of the - :meth:`~flask.Flask.run` method to look like this:: + If you have the debugger disabled or trust the users on your network, + you can make the server publicly available simply by adding + ``--host=0.0.0.0`` to the command line:: - app.run(host='0.0.0.0') + flask -a hello run --host=0.0.0.0 This tells your operating system to listen on all public IPs. +What to do if the Server does not Start +--------------------------------------- + +In case the ``python -m flask`` fails or ``flask`` does not exist, +there are multiple reasons this might be the case. First of all you need +to look at the error message. + +Old Version of Flask +```````````````````` + +Versions of Flask older than 1.0 use to have different ways to start the +application. In short, the ``flask`` command did not exist, and +neither did ``python -m flask``. In that case you have two options: +either upgrade to newer Flask versions or have a look at the :ref:`server` +docs to see the alternative method for running a server. + +Python older 2.7 +```````````````` + +In case you have a version of Python older than 2.7 ``python -m flask`` +does not work. You can either use ``flask`` or ``python -m +flask.cli`` as an alternative. This is because Python before 2.7 does no +permit packages to act as executable modules. For more information see +:ref:`cli`. + +Invalid Import Name +``````````````````` + +The ``-a`` argument to ``flask`` is the name of the module to import. In +case that module is incorrectly named you will get an import error upon +start (or if debug is enabled when you navigate to the application). It +will tell you what it tried to import and why it failed. + +The most common reason is a typo or because you did not actually create an +``app`` object. .. _debug-mode: Debug Mode ---------- -The :meth:`~flask.Flask.run` method is nice to start a local -development server, but you would have to restart it manually after each -change to your code. That is not very nice and Flask can do better. If -you enable debug support the server will reload itself on code changes, -and it will also provide you with a helpful debugger if things go wrong. +The ``flask`` script is nice to start a local development server, but +you would have to restart it manually after each change to your code. +That is not very nice and Flask can do better. If you enable debug +support the server will reload itself on code changes, and it will also +provide you with a helpful debugger if things go wrong. -There are two ways to enable debugging. Either set that flag on the -application object:: +There are different ways to enable the debug mode. The most obvious one +is the ``--debug`` parameter to the ``flask`` command:: - app.debug = True - app.run() + flask --debug -a hello run -Or pass it as a parameter to run:: +This does the following things: - app.run(debug=True) +1. it activates the debugger +2. it activates the automatic reloader +3. it enables the debug mode on the Flask application. -Both methods have the exact same effect. +There are more parameters that are explained in the :ref:`server` docs. .. admonition:: Attention @@ -236,9 +273,9 @@ below. It tells Flask to behave as though it is handling a request, even though we are interacting with it through a Python shell. Have a look at the explanation below. :ref:`context-locals`). -Why would you want to build URLs using the URL reversing function :func:`~flask.url_for` -instead of hard-coding them into your templates? There are three good reasons -for this: +Why would you want to build URLs using the URL reversing function +:func:`~flask.url_for` instead of hard-coding them into your templates? +There are three good reasons for this: 1. Reversing is often more descriptive than hard-coding the URLs. More importantly, it allows you to change URLs in one go, without having to diff --git a/docs/server.rst b/docs/server.rst new file mode 100644 index 00000000..bd79b7d0 --- /dev/null +++ b/docs/server.rst @@ -0,0 +1,50 @@ +.. _server: + +Development Server +================== + +.. currentmodule:: flask + +Starting with Flask 1.0 there are multiple built-in ways to run a +development server. The best one is the ``flask`` command line utility +but you can also continue using the :meth:`Flask.run` method. + +Command Line +------------ + +The ``flask`` command line script (:ref:`cli`) is strongly recommende for +development because it provides a superior reload experience due to how it +loads the application. The basic usage is like this:: + + $ flask -a my_application --debug run + +This will enable the debugger, the reloader and then start the server on +*http://localhost:5000/*. + +The individual features of the server can be controlled by passing more +arguments to the ``run`` option. For instance the reloader can be +disabled:: + + $ flask -a my_application --debug run --no-reload + +In Code +------- + +The alternative way to start the application is through the +:meth:`Flask.run` method. This will immediately launch a local server +exactly the same way the ``flask`` script does. + +Example:: + + if __name__ == '__main__': + app.run() + +This works well for the common case but it does not work well for +development which is why from Flask 1.0 onwards the ``flask`` method is +recommended. The reason for this is that due to how the reload mechanism +works there are some bizarre side-effects (like executing certain code +twice, sometimes crashing without message or dieing when a syntax or +import error happens). + +It is however still a perfectly valid method for invoking a non automatic +reloading application. diff --git a/docs/shell.rst b/docs/shell.rst index 61b9dc05..f4522c1a 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -26,6 +26,16 @@ context. Generally it's recommended that you read the :ref:`request-context` chapter of the documentation first. +Command Line Interface +---------------------- + +Starting with Flask 1.0 the recommended way to work with the shell is the +``flask shell`` command which does a lot of this automatically for you. +For instance the shell is automatically initialized with a loaded +application context. + +For more information see :ref:`cli`. + Creating a Request Context -------------------------- From 984142072bc268008265eee9af4f8891e3c77737 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:31:21 +0200 Subject: [PATCH 0169/2502] Added missing documentation page --- docs/cli.rst | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 docs/cli.rst diff --git a/docs/cli.rst b/docs/cli.rst new file mode 100644 index 00000000..b0833c22 --- /dev/null +++ b/docs/cli.rst @@ -0,0 +1,101 @@ +.. _cli: + +Command Line Interface +====================== + +.. versionadded:: 1.0 + +.. currentmodule:: flask + +One of the nice new features in Flask 1.0 is the built-in integration of +the `click `_ command line interface. This +enables a wide range of new features for the Flask ecosystem and your own +applications. + +Basic Usage +----------- + +After installation of Flask you will now find a ``flask`` script installed +into your virtualenv. If you don't want to install Flask or you have a +special use-case you can also use ``python -mflask`` to accomplish exactly +the same. + +The way this script works is by providing access to all the commands on +your Flask application's :attr:`Flask.cli` instance as well as some +built-in commands that are always there. Flask extensions can also +register more commands there if they so desire. + +For the ``flask`` script to work, an application needs to be discovered. +The two most common ways are either an environment variable +(``FLASK_APP``) or the ``--app`` / ``-a`` parameter. It should be the +import path for your application or the path to a Python file. In the +latter case Flask will attempt to setup the Python path for you +automatically and discover the module name but that might not always work. + +In that imported file the name of the app needs to be called ``app`` or +optionally be specified after a colon. + +Given a ``hello.py`` file with the application in it named ``app`` this is +how it can be run. + +Environment variables (On Windows use ``set`` instead of ``export``):: + + export FLASK_APP=hello + flask run + +Parameters:: + + flask --app=hello run + +File names:: + + flask --app=hello.py run + +Virtualenv Integration +---------------------- + +If you are constantly working with a virtualenv you can also put the +``export FLASK_APP`` into your ``activate`` script by adding it to the +bottom of the file. That way every time you activate your virtualenv you +automatically also activate the correct application name. + +Debug Flag +---------- + +The ``flask`` script can be run with ``--debug`` or ``--no-debug`` to +automatically flip the debug flag of the application. This can also be +configured by setting ``FLASK_DEBUG`` to ``1`` or ``0``. + +Running a Shell +--------------- + +To run an interactive Python shell you can use the ``shell`` command:: + + flask --app=hello shell + +This will start up an interactive Python shell, setup the correct +application context and setup the local variables in the shell. This is +done by invoking the :meth:`Flask.make_shell_context` method of the +application. By default you have access to your ``app`` and :data:`g`. + +Custom Commands +--------------- + +If you want to add more commands to the shell script you can do this +easily. Flask uses `click`_ for the command interface which makes +creating custom commands very easy. For instance if you want a shell +command to initialize the database you can do this:: + + from flask import Flask + + app = Flask(__name__) + + @app.cli.command() + def initdb(): + """Initialize the database.""" + print 'Init the db' + +The command will then show up on the command line:: + + $ flask -a hello.py initdb + Init the db From ff034e42cfb665dc9123c95e51ad3d1455322bd5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:54:13 +0200 Subject: [PATCH 0170/2502] Added a missing docstring --- flask/cli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 7e982878..58e07521 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -53,6 +53,9 @@ def find_best_app(module): def prepare_exec_for_file(filename): + """Given a filename this will try to calculate the python path, add it + to the search path and return the actual module name that is expected. + """ module = [] # Chop off file extensions or package markers @@ -187,8 +190,7 @@ class FlaskClickGroup(click.Group): def __init__(self, help=None): def set_app_id(ctx, value): if value is not None: - if os.path.isfile(value) or os.sep in value or \ - os.altsep is not None and os.altsep in value: + if os.path.isfile(value): value = prepare_exec_for_file(value) ctx.obj.app_import_path = value def set_debug(ctx, value): From bacdd076bd544ca82d05111e3a427e98e4d4c996 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 15:15:58 +0200 Subject: [PATCH 0171/2502] Add "." to sys.path by default --- flask/cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flask/cli.py b/flask/cli.py index 58e07521..ccfb7e17 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -192,6 +192,8 @@ class FlaskClickGroup(click.Group): if value is not None: if os.path.isfile(value): value = prepare_exec_for_file(value) + elif '.' not in sys.path: + sys.path.insert(0, '.') ctx.obj.app_import_path = value def set_debug(ctx, value): ctx.obj.debug = value From a3a5075a942a321ccf67c7511822e62dc1d5858f Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 15:18:27 +0200 Subject: [PATCH 0172/2502] Updated tutorial to the flask script --- docs/tutorial/dbinit.rst | 46 +++++++++++++++++--------------------- docs/tutorial/setup.rst | 18 +++++---------- examples/flaskr/.gitignore | 1 + examples/flaskr/flaskr.py | 20 +++++++---------- 4 files changed, 36 insertions(+), 49 deletions(-) create mode 100644 examples/flaskr/.gitignore diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 980caca0..0d92f5be 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -20,28 +20,25 @@ require that we provide the path to the database which can introduce errors. It's a good idea to add a function that initializes the database for you to the application. -To do this we can create a function called `init_db` that initializes the -database. Let me show you the code first. Just add this function below -the `connect_db` function in `flaskr.py`:: +To do this we can create a function and hook it into the ``flask`` command +that initializes the database. Let me show you the code first. Just add +this function below the `connect_db` function in `flaskr.py`:: - def init_db(): - with app.app_context(): - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() + @app.cli.command() + def initdb(): + """Initializes the database.""" + db = get_db() + with app.open_resource('schema.sql', mode='r') as f: + db.cursor().executescript(f.read()) + db.commit() + print 'Initialized the database.' -So what's happening here? Remember how we learned last chapter that the -application context is created every time a request comes in? Here we -don't have a request yet, so we need to create the application context by -hand. Without an application context the :data:`~flask.g` object does not -know yet to which application it becomes as there could be more than one! - -The ``with app.app_context()`` statement establishes the application -context for us. In the body of the with statement the :data:`~flask.g` -object will be associated with ``app``. At the end of the with statement -the association is released and all teardown functions are executed. This -means that our database connection is disconnected after the commit. +The ``app.cli.command()`` decorator registers a new command with the +``flask`` script. When the command executes Flask will automatically +create a application context for us bound to the right application. +Within the function we can then access :attr:`flask.g` and other things as +we would expect. When the script ends, the application context tears down +and the database connection is released. The :func:`~flask.Flask.open_resource` method of the application object is a convenient helper function that will open a resource that the @@ -54,16 +51,15 @@ On that cursor there is a method to execute a complete script. Finally we only have to commit the changes. SQLite 3 and other transactional databases will not commit unless you explicitly tell it to. -Now it is possible to create a database by starting up a Python shell and -importing and calling that function:: +Now it is possible to create a database with the ``flask`` script:: ->>> from flaskr import init_db ->>> init_db() + flask --app=flaskr initdb + Initialized the database. .. admonition:: Troubleshooting If you get an exception later that a table cannot be found check that - you did call the `init_db` function and that your table names are + you did execute the `initdb` command and that your table names are correct (singular vs. plural for example). Continue with :ref:`tutorial-views` diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 65d4b3f2..0b76e105 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -29,7 +29,6 @@ config from the same file, in `flaskr.py`:: # Load default config and override config from an environment variable app.config.update(dict( DATABASE=os.path.join(app.root_path, 'flaskr.db'), - DEBUG=True, SECRET_KEY='development key', USERNAME='admin', PASSWORD='default' @@ -71,10 +70,7 @@ module. Flask will the initialize the variable from that module. Note that in all cases only variable names that are uppercase are considered. The ``SECRET_KEY`` is needed to keep the client-side sessions secure. -Choose that key wisely and as hard to guess and complex as possible. The -debug flag enables or disables the interactive debugger. *Never leave -debug mode activated in a production system*, because it will allow users to -execute code on the server! +Choose that key wisely and as hard to guess and complex as possible. We will also add a method that allows for easily connecting to the specified database. This can be used to open a connection on request and @@ -92,16 +88,14 @@ tuples. rv.row_factory = sqlite3.Row return rv -Finally we just add a line to the bottom of the file that fires up the -server if we want to run that file as a standalone application:: - - if __name__ == '__main__': - app.run() - With that out of the way you should be able to start up the application without problems. Do this with the following command:: - python flaskr.py + flask --app=flaskr --debug run + +The ``--debug`` flag enables or disables the interactive debugger. *Never +leave debug mode activated in a production system*, because it will allow +users to execute code on the server! You will see a message telling you that server has started along with the address at which you can access it. diff --git a/examples/flaskr/.gitignore b/examples/flaskr/.gitignore new file mode 100644 index 00000000..fb46a3af --- /dev/null +++ b/examples/flaskr/.gitignore @@ -0,0 +1 @@ +flaskr.db diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index 384162af..2cd2fd19 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -37,13 +37,14 @@ def connect_db(): return rv -def init_db(): +@app.cli.command() +def initdb(): """Creates the database tables.""" - with app.app_context(): - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() + db = get_db() + with app.open_resource('schema.sql', mode='r') as f: + db.cursor().executescript(f.read()) + db.commit() + print('Initialized the database.') def get_db(): @@ -76,7 +77,7 @@ def add_entry(): abort(401) db = get_db() db.execute('insert into entries (title, text) values (?, ?)', - [request.form['title'], request.form['text']]) + [request.form['title'], request.form['text']]) db.commit() flash('New entry was successfully posted') return redirect(url_for('show_entries')) @@ -102,8 +103,3 @@ def logout(): session.pop('logged_in', None) flash('You were logged out') return redirect(url_for('show_entries')) - - -if __name__ == '__main__': - init_db() - app.run() From ed9e458850a548ceb6027bf25a657f26595656ff Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Mon, 28 Apr 2014 11:26:18 -0400 Subject: [PATCH 0173/2502] Fix a docstring type in cli.py. --- flask/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index ccfb7e17..4b4d934c 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -99,7 +99,7 @@ def locate_app(app_id, debug=None): class DispatchingApp(object): - """Special applicationt that dispatches to a flask application which + """Special application that dispatches to a flask application which is imported by name on first request. This is safer than importing the application upfront because it means that we can forward all errors for import problems into the browser as error. From a3ad5405a68f312f077e8d6271e694f6925c0864 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 29 Apr 2014 01:03:32 +0200 Subject: [PATCH 0174/2502] Updated documentation once more for new cli. --- docs/cli.rst | 29 +++++++++++++++++++++++++++++ docs/patterns/appfactories.rst | 9 +++++++-- docs/patterns/flashing.rst | 4 ---- flask/cli.py | 5 +++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index b0833c22..0ac578e9 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -99,3 +99,32 @@ The command will then show up on the command line:: $ flask -a hello.py initdb Init the db + +Factory Functions +----------------- + +In case you are using factory functions to create your application (see +:ref:`app-factories`) you will discover that the ``flask`` command cannot +work with them directly. Flask won't be able to figure out how to +instanciate your application properly by itself. Because of this reason +the recommendation is to create a separate file that instanciates +applications. + +For instance if you have a factory function that creates an application +from a filename you could make a separate file that creates such an +application from an environment variable. + +For instance this could be a file named ``autoapp.py`` with these +contents:: + + import os + from yourapplication import create_app + app = create_app(os.environ['YOURAPPLICATION_CONFIG']) + +Once this has happened you can make the flask command automatically pick +it up:: + + export YOURAPPLICATION_CONFIG=/path/to/config.cfg + export FLASK_APP=/path/to/autoapp.py + +From this point onwards ``flask`` will find your application. diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index 25a90212..2f387f1a 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -90,11 +90,16 @@ Using Applications ------------------ So to use such an application you then have to create the application -first. Here an example `run.py` file that runs such an application:: +first in a separate file otherwise the ``flask`` command won't be able +to find it. Here an example `exampleapp.py` file that creates such +an application:: from yourapplication import create_app app = create_app('/path/to/config.cfg') - app.run() + +It can then be used with the ``flask`` command:: + + flask --app=exampleapp run Factory Improvements -------------------- diff --git a/docs/patterns/flashing.rst b/docs/patterns/flashing.rst index 5f3b02eb..475b49fa 100644 --- a/docs/patterns/flashing.rst +++ b/docs/patterns/flashing.rst @@ -38,10 +38,6 @@ So here is a full example:: return redirect(url_for('index')) return render_template('login.html', error=error) - if __name__ == "__main__": - app.run() - - And here the ``layout.html`` template which does the magic: .. sourcecode:: html+jinja diff --git a/flask/cli.py b/flask/cli.py index ccfb7e17..89d29fe7 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -63,6 +63,11 @@ def prepare_exec_for_file(filename): filename = filename[:-3] elif os.path.split(filename)[1] == '__init__.py': filename = os.path.dirname(filename) + else: + raise NoAppException('The file provided (%s) does exist but is not a ' + 'valid Python file. This means that it cannot ' + 'be used as application. Please change the ' + 'extension to .py' % filename) filename = os.path.realpath(filename) dirpath = filename From 9ab59871007c12fad03f43ac08f889e4c080e64d Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 29 Apr 2014 01:48:31 +0200 Subject: [PATCH 0175/2502] Updated the examples to be cli based --- examples/blueprintexample/blueprintexample.py | 6 +----- examples/flaskr/README | 13 ++++++++++--- examples/jqueryexample/jqueryexample.py | 4 ---- examples/minitwit/README | 13 ++++++++----- examples/minitwit/minitwit.py | 19 +++++++------------ examples/persona/persona.py | 4 ---- 6 files changed, 26 insertions(+), 33 deletions(-) diff --git a/examples/blueprintexample/blueprintexample.py b/examples/blueprintexample/blueprintexample.py index bc0e41d4..925f4845 100644 --- a/examples/blueprintexample/blueprintexample.py +++ b/examples/blueprintexample/blueprintexample.py @@ -4,8 +4,4 @@ from simple_page.simple_page import simple_page app = Flask(__name__) app.register_blueprint(simple_page) # Blueprint can be registered many times -app.register_blueprint(simple_page, url_prefix='/pages') - - -if __name__ == '__main__': - app.run(debug=True) \ No newline at end of file +app.register_blueprint(simple_page, url_prefix='/pages') diff --git a/examples/flaskr/README b/examples/flaskr/README index 9b5b9215..fc555388 100644 --- a/examples/flaskr/README +++ b/examples/flaskr/README @@ -13,9 +13,16 @@ export an FLASKR_SETTINGS environment variable pointing to a configuration file. - 2. now you can run the flaskr.py file with your - python interpreter and the application will - greet you on http://localhost:5000/ + 2. initialize the database with this command: + + flask --app=flaskr initdb + + 3. now you can run flaskr: + + flask --app=flaskr run + + the application will greet you on + http://localhost:5000/ ~ Is it tested? diff --git a/examples/jqueryexample/jqueryexample.py b/examples/jqueryexample/jqueryexample.py index 08164119..034cc495 100644 --- a/examples/jqueryexample/jqueryexample.py +++ b/examples/jqueryexample/jqueryexample.py @@ -23,7 +23,3 @@ def add_numbers(): @app.route('/') def index(): return render_template('index.html') - - -if __name__ == '__main__': - app.run() diff --git a/examples/minitwit/README b/examples/minitwit/README index ab946295..873ceb8e 100644 --- a/examples/minitwit/README +++ b/examples/minitwit/README @@ -14,13 +14,16 @@ export an MINITWIT_SETTINGS environment variable pointing to a configuration file. - 2. fire up a python shell and run this: + 2. fire up a shell and run this: - >>> from minitwit import init_db; init_db() + flask --app=minitwit initdb - 3. now you can run the minitwit.py file with your - python interpreter and the application will - greet you on http://localhost:5000/ + 3. now you can run minitwit: + + flask --app=minitwit run + + the application will greet you on + http://localhost:5000/ ~ Is it tested? diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py index 913d4522..0a85247b 100644 --- a/examples/minitwit/minitwit.py +++ b/examples/minitwit/minitwit.py @@ -49,13 +49,13 @@ def close_database(exception): top.sqlite_db.close() -def init_db(): +@app.cli.command() +def initdb(): """Creates the database tables.""" - with app.app_context(): - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() + db = get_db() + with app.open_resource('schema.sql', mode='r') as f: + db.cursor().executescript(f.read()) + db.commit() def query_db(query, args=(), one=False): @@ -217,7 +217,7 @@ def register(): if not request.form['username']: error = 'You have to enter a username' elif not request.form['email'] or \ - '@' not in request.form['email']: + '@' not in request.form['email']: error = 'You have to enter a valid email address' elif not request.form['password']: error = 'You have to enter a password' @@ -248,8 +248,3 @@ def logout(): # add some filters to jinja app.jinja_env.filters['datetimeformat'] = format_datetime app.jinja_env.filters['gravatar'] = gravatar_url - - -if __name__ == '__main__': - init_db() - app.run() diff --git a/examples/persona/persona.py b/examples/persona/persona.py index d56f299a..7374c3af 100644 --- a/examples/persona/persona.py +++ b/examples/persona/persona.py @@ -53,7 +53,3 @@ def logout_handler(): """ session.clear() return 'OK' - - -if __name__ == '__main__': - app.run() From e059bf311c6a75150275b434c53544f7528749bc Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 1 May 2014 13:44:11 +0100 Subject: [PATCH 0176/2502] Improved support for composable cli --- flask/cli.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index b8450de1..45e68ec5 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -139,9 +139,9 @@ class ScriptInfo(object): to click. """ - def __init__(self): - self.app_import_path = None - self.debug = None + def __init__(self, app_import_path=None, debug=None): + self.app_import_path = app_import_path + self.debug = debug self._loaded_app = None def get_app_import_path(self): @@ -187,7 +187,7 @@ def without_appcontext(f): return f -class FlaskClickGroup(click.Group): +class FlaskGroup(click.Group): """Special subclass of the a regular click group that supports loading more commands from the configured Flask app. """ @@ -244,7 +244,7 @@ class FlaskClickGroup(click.Group): self, ctx, cmd, cmd_name, args) -cli = FlaskClickGroup(help='''\ +cli = FlaskGroup(help='''\ This shell command acts as general utility script for Flask applications. It loads the application configured (either through the FLASK_APP environment @@ -346,7 +346,7 @@ def main(as_module=False): # the reloader can properly operate. sys.argv = ['-m', this_module] + sys.argv[1:] else: - name = 'flask' + name = None cli.main(args=args, prog_name=name, obj=ScriptInfo(), auto_envvar_prefix='FLASK') From d2d8e66130f8aabf8e0e4b80a42352f8ef93c347 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 2 May 2014 11:46:04 +0100 Subject: [PATCH 0177/2502] Fixed flask tests --- examples/flaskr/flaskr.py | 11 ++++++++--- examples/flaskr/flaskr_tests.py | 3 ++- examples/minitwit/minitwit.py | 12 +++++++++--- examples/minitwit/minitwit_tests.py | 3 ++- flask/cli.py | 4 ++-- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index 2cd2fd19..a38a04eb 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -37,13 +37,18 @@ def connect_db(): return rv -@app.cli.command() -def initdb(): - """Creates the database tables.""" +def init_db(): + """Initializes the database.""" db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() + + +@app.cli.command('initdb') +def initdb_command(): + """Creates the database tables.""" + init_db() print('Initialized the database.') diff --git a/examples/flaskr/flaskr_tests.py b/examples/flaskr/flaskr_tests.py index ab08a60a..b90a7be7 100644 --- a/examples/flaskr/flaskr_tests.py +++ b/examples/flaskr/flaskr_tests.py @@ -21,7 +21,8 @@ class FlaskrTestCase(unittest.TestCase): self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() flaskr.app.config['TESTING'] = True self.app = flaskr.app.test_client() - flaskr.init_db() + with flaskr.app.app_context(): + flaskr.init_db() def tearDown(self): """Get rid of the database again after each test.""" diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py index 0a85247b..f772b682 100644 --- a/examples/minitwit/minitwit.py +++ b/examples/minitwit/minitwit.py @@ -49,15 +49,21 @@ def close_database(exception): top.sqlite_db.close() -@app.cli.command() -def initdb(): - """Creates the database tables.""" +def init_db(): + """Initializes the database.""" db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() +@app.cli.command('initdb') +def initdb_command(): + """Creates the database tables.""" + init_db() + print('Initialized the database.') + + def query_db(query, args=(), one=False): """Queries the database and returns a list of dictionaries.""" cur = get_db().execute(query, args) diff --git a/examples/minitwit/minitwit_tests.py b/examples/minitwit/minitwit_tests.py index 9b027629..0a1a3f67 100644 --- a/examples/minitwit/minitwit_tests.py +++ b/examples/minitwit/minitwit_tests.py @@ -20,7 +20,8 @@ class MiniTwitTestCase(unittest.TestCase): """Before each test, set up a blank database""" self.db_fd, minitwit.app.config['DATABASE'] = tempfile.mkstemp() self.app = minitwit.app.test_client() - minitwit.init_db() + with minitwit.app.app_context(): + minitwit.init_db() def tearDown(self): """Get rid of the database again after each test.""" diff --git a/flask/cli.py b/flask/cli.py index 45e68ec5..0b37e3f9 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -199,9 +199,9 @@ class FlaskGroup(click.Group): value = prepare_exec_for_file(value) elif '.' not in sys.path: sys.path.insert(0, '.') - ctx.obj.app_import_path = value + ctx.ensure_object(ScriptInfo).app_import_path = value def set_debug(ctx, value): - ctx.obj.debug = value + ctx.ensure_object(ScriptInfo).debug = value click.Group.__init__(self, help=help, params=[ click.Option(['-a', '--app'], From b9013ede2213e463c1b6d49b3651c796debc1ea4 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 2 May 2014 11:50:00 +0100 Subject: [PATCH 0178/2502] Clarified how to test with click --- docs/testing.rst | 3 ++- docs/tutorial/dbinit.rst | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index d31be217..95b2021a 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -43,7 +43,8 @@ In order to test the application, we add a second module self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() flaskr.app.config['TESTING'] = True self.app = flaskr.app.test_client() - flaskr.init_db() + with flaskr.app.app_context(): + flaskr.init_db() def tearDown(self): os.close(self.db_fd) diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 0d92f5be..10404b3a 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -24,13 +24,16 @@ To do this we can create a function and hook it into the ``flask`` command that initializes the database. Let me show you the code first. Just add this function below the `connect_db` function in `flaskr.py`:: - @app.cli.command() - def initdb(): - """Initializes the database.""" + def init_db(): db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() + + @app.cli.command('initdb') + def initdb_command(): + """Initializes the database.""" + init_db() print 'Initialized the database.' The ``app.cli.command()`` decorator registers a new command with the @@ -40,6 +43,10 @@ Within the function we can then access :attr:`flask.g` and other things as we would expect. When the script ends, the application context tears down and the database connection is released. +We want to keep an actual functions around that initializes the database +though so that we can easily create databases in unittests later. (For +more information see :ref:`testing`.) + The :func:`~flask.Flask.open_resource` method of the application object is a convenient helper function that will open a resource that the application provides. This function opens a file from the resource From 4532e89efe9ef1161d4cdbd12687dac0c19c49b7 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 11:34:20 +0200 Subject: [PATCH 0179/2502] Refactored script info to allow more advanced patterns. --- flask/cli.py | 60 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 0b37e3f9..2bcfdee7 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -110,38 +110,44 @@ class DispatchingApp(object): errors for import problems into the browser as error. """ - def __init__(self, app_id, debug=None, use_eager_loading=False): - self.app_id = app_id - self.app = None - self.debug = debug + def __init__(self, loader, use_eager_loading=False): + self.loader = loader + self._app = None self._lock = Lock() if use_eager_loading: self._load_unlocked() def _load_unlocked(self): - self.app = rv = locate_app(self.app_id, self.debug) + self._app = rv = self.loader() return rv def __call__(self, environ, start_response): - if self.app is not None: - return self.app(environ, start_response) + if self._app is not None: + return self._app(environ, start_response) with self._lock: - if self.app is not None: - rv = self.app + if self._app is not None: + rv = self._app else: rv = self._load_unlocked() return rv(environ, start_response) +def _no_such_app(): + raise NoAppException('Could not locate Flask application. ' + 'You did not provide FLASK_APP or the ' + '--app parameter.') + + class ScriptInfo(object): """Help object to deal with Flask applications. This is usually not necessary to interface with as it's used internally in the dispatching to click. """ - def __init__(self, app_import_path=None, debug=None): + def __init__(self, app_import_path=None, debug=None, load_callback=None): self.app_import_path = app_import_path self.debug = debug + self.load_callback = load_callback self._loaded_app = None def get_app_import_path(self): @@ -150,18 +156,33 @@ class ScriptInfo(object): """ if self.app_import_path is not None: return self.app_import_path - raise NoAppException('Could not locate application. ' - 'You did not provide FLASK_APP or the ' - '--app parameter.') + _no_such_app() def load_app(self): """Loads the app (if not yet loaded) and returns it.""" if self._loaded_app is not None: return self._loaded_app - rv = locate_app(self.get_app_import_path(), self.debug) + if self.load_callback is not None: + rv = self.load_callback() + else: + rv = locate_app(self.get_app_import_path(), self.debug) self._loaded_app = rv return rv + def make_wsgi_app(self, use_eager_loading=False): + """Returns a WSGI app that loads the actual application at a + later stage. + """ + if self.app_import_path is not None: + def loader(): + return locate_app(self.app_import_path, self.debug) + else: + if self.load_callback is None: + _no_such_app() + def loader(): + return self.load_callback() + return DispatchingApp(loader, use_eager_loading=use_eager_loading) + @contextmanager def conditional_context(self, with_context=True): """Creates an application context or not, depending on the @@ -281,7 +302,6 @@ def run_command(info, host, port, reload, debugger, eager_loading, with_threads): """Runs a local development server for the Flask application.""" from werkzeug.serving import run_simple - app_id = info.get_app_import_path() if reload is None: reload = info.debug if debugger is None: @@ -289,14 +309,20 @@ def run_command(info, host, port, reload, debugger, eager_loading, if eager_loading is None: eager_loading = not reload + app = info.make_wsgi_app(use_eager_loading=eager_loading) + # Extra startup messages. This depends a but on Werkzeug internals to # not double execute when the reloader kicks in. if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': - print(' * Serving Flask app "%s"' % app_id) + # If we have an import path we can print it out now which can help + # people understand what's being served. If we do not have an + # import path because the app was loaded through a callback then + # we won't print anything. + if info.app_import_path is not None: + print(' * Serving Flask app "%s"' % info.app_import_path) if info.debug is not None: print(' * Forcing debug %s' % (info.debug and 'on' or 'off')) - app = DispatchingApp(app_id, info.debug, eager_loading) run_simple(host, port, app, use_reloader=reload, use_debugger=debugger, threaded=with_threads) From ed7b4ccac1b646aa725007db7fd3ec2d359beaf4 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 11:36:49 +0200 Subject: [PATCH 0180/2502] Refactored loading logic to super properly. --- flask/cli.py | 67 +++++++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 2bcfdee7..adca5ca1 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -208,7 +208,41 @@ def without_appcontext(f): return f -class FlaskGroup(click.Group): +class ContextGroupMixin(object): + + def get_command(self, ctx, name): + info = ctx.find_object(ScriptInfo) + # Find the command in the application first, if we can find it. + # If the app is not available, we just ignore this silently. + try: + rv = info.load_app().cli.get_command(ctx, name) + if rv is not None: + return rv + except NoAppException: + pass + return super(ContextGroupMixin, self).get_command(ctx, name) + + def list_commands(self, ctx): + # The commands available is the list of both the application (if + # available) plus the builtin commands. + rv = set(super(ContextGroupMixin, self).list_commands(ctx)) + info = ctx.find_object(ScriptInfo) + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except NoAppException: + pass + return sorted(rv) + + def invoke_subcommand(self, ctx, cmd, cmd_name, args): + with_context = cmd.callback is None or \ + not getattr(cmd.callback, '__flask_without_appcontext__', False) + + with ctx.find_object(ScriptInfo).conditional_context(with_context): + return super(ContextGroupMixin, self).invoke_subcommand( + ctx, cmd, cmd_name, args) + + +class FlaskGroup(ContextGroupMixin, click.Group): """Special subclass of the a regular click group that supports loading more commands from the configured Flask app. """ @@ -233,37 +267,6 @@ class FlaskGroup(click.Group): default=None, callback=set_debug) ]) - def get_command(self, ctx, name): - info = ctx.find_object(ScriptInfo) - # Find the command in the application first, if we can find it. - # If the app is not available, we just ignore this silently. - try: - rv = info.load_app().cli.get_command(ctx, name) - if rv is not None: - return rv - except NoAppException: - pass - return click.Group.get_command(self, ctx, name) - - def list_commands(self, ctx): - # The commands available is the list of both the application (if - # available) plus the builtin commands. - rv = set(click.Group.list_commands(self, ctx)) - info = ctx.find_object(ScriptInfo) - try: - rv.update(info.load_app().cli.list_commands(ctx)) - except NoAppException: - pass - return sorted(rv) - - def invoke_subcommand(self, ctx, cmd, cmd_name, args): - with_context = cmd.callback is None or \ - not getattr(cmd.callback, '__flask_without_appcontext__', False) - - with ctx.find_object(ScriptInfo).conditional_context(with_context): - return click.Group.invoke_subcommand( - self, ctx, cmd, cmd_name, args) - cli = FlaskGroup(help='''\ This shell command acts as general utility script for Flask applications. From 81576c236a4dde33aeed13c84abed0ad2e796c2f Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 13:27:41 +0200 Subject: [PATCH 0181/2502] Further refactored script system to allow more customization for special apps. --- flask/cli.py | 190 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 116 insertions(+), 74 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index adca5ca1..aa385011 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -144,57 +144,62 @@ class ScriptInfo(object): to click. """ - def __init__(self, app_import_path=None, debug=None, load_callback=None): + def __init__(self, app_import_path=None, debug=None, create_app=None): self.app_import_path = app_import_path self.debug = debug - self.load_callback = load_callback + self.create_app = create_app self._loaded_app = None - def get_app_import_path(self): - """Return the actual application import path or fails if it is - not yet set. - """ - if self.app_import_path is not None: - return self.app_import_path - _no_such_app() - def load_app(self): - """Loads the app (if not yet loaded) and returns it.""" + """Loads the Flask app (if not yet loaded) and returns it.""" if self._loaded_app is not None: return self._loaded_app - if self.load_callback is not None: - rv = self.load_callback() + if self.create_app is not None: + rv = self.create_app(self) else: - rv = locate_app(self.get_app_import_path(), self.debug) + if self.app_import_path is None: + _no_such_app() + rv = locate_app(self.app_import_path, self.debug) self._loaded_app = rv return rv def make_wsgi_app(self, use_eager_loading=False): - """Returns a WSGI app that loads the actual application at a - later stage. + """Returns a WSGI app that loads the actual application at a later + stage (on first request). This has the advantage over + :meth:`load_app` that if used with a WSGI server, it will allow + the server to intercept errors later during request handling + instead of dying a horrible death. + + If eager loading is disabled the loading will happen immediately. """ if self.app_import_path is not None: def loader(): return locate_app(self.app_import_path, self.debug) else: - if self.load_callback is None: + if self.create_app is None: _no_such_app() def loader(): - return self.load_callback() + return self.create_app(self) return DispatchingApp(loader, use_eager_loading=use_eager_loading) @contextmanager def conditional_context(self, with_context=True): - """Creates an application context or not, depending on the - given parameter but always works as context manager. + """Creates an application context or not, depending on the given + parameter but always works as context manager. This is just a + shortcut for a common operation. """ if with_context: - with self.load_app().app_context() as ctx: + with self.load_app(self).app_context() as ctx: yield ctx else: yield None +#: A special decorator that informs a click callback to be passed the +#: script info object as first argument. This is normally not useful +#: unless you implement very special commands like the run command which +#: does not want the application to be loaded yet. This can be combined +#: with the :func:`without_appcontext` decorator. pass_script_info = click.make_pass_decorator(ScriptInfo) @@ -208,10 +213,56 @@ def without_appcontext(f): return f -class ContextGroupMixin(object): +class FlaskGroup(click.Group): + """Special subclass of the a regular click group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced usecases for which it makes sense to create an + instance of this. + + :param add_default_options: if this is True the app and debug option + is automatically added. + """ + + def __init__(self, add_default_options=True, + add_default_commands=True, + create_app=None, **extra): + click.Group.__init__(self, **extra) + self.create_app = create_app + if add_default_options: + self.add_app_option() + self.add_debug_option() + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + + def add_app_option(self): + """Adds an option to the default command that defines an import + path that points to an application. + """ + def set_app_id(ctx, value): + if value is not None: + if os.path.isfile(value): + value = prepare_exec_for_file(value) + elif '.' not in sys.path: + sys.path.insert(0, '.') + ctx.ensure_object(ScriptInfo).app_import_path = value + + self.params.append(click.Option(['-a', '--app'], + help='The application to run', + callback=set_app_id, is_eager=True)) + + def add_debug_option(self): + """Adds an option that controls the debug flag.""" + def set_debug(ctx, value): + ctx.ensure_object(ScriptInfo).debug = value + + self.params.append(click.Option(['--debug/--no-debug'], + help='Enable or disable debug mode.', + default=None, callback=set_debug)) def get_command(self, ctx, name): - info = ctx.find_object(ScriptInfo) + info = ctx.ensure_object(ScriptInfo) # Find the command in the application first, if we can find it. # If the app is not available, we just ignore this silently. try: @@ -220,13 +271,13 @@ class ContextGroupMixin(object): return rv except NoAppException: pass - return super(ContextGroupMixin, self).get_command(ctx, name) + return click.Group.get_command(self, ctx, name) def list_commands(self, ctx): # The commands available is the list of both the application (if # available) plus the builtin commands. - rv = set(super(ContextGroupMixin, self).list_commands(ctx)) - info = ctx.find_object(ScriptInfo) + rv = set(click.Group.list_commands(self, ctx)) + info = ctx.ensure_object(ScriptInfo) try: rv.update(info.load_app().cli.list_commands(ctx)) except NoAppException: @@ -238,52 +289,19 @@ class ContextGroupMixin(object): not getattr(cmd.callback, '__flask_without_appcontext__', False) with ctx.find_object(ScriptInfo).conditional_context(with_context): - return super(ContextGroupMixin, self).invoke_subcommand( - ctx, cmd, cmd_name, args) + return click.Group.invoke_subcommand( + self, ctx, cmd, cmd_name, args) + + def main(self, *args, **kwargs): + obj = kwargs.get('obj') + if obj is None: + obj = ScriptInfo(create_app=self.create_app) + kwargs['obj'] = obj + kwargs.setdefault('auto_envvar_prefix', 'FLASK') + return click.Group.main(self, *args, **kwargs) -class FlaskGroup(ContextGroupMixin, click.Group): - """Special subclass of the a regular click group that supports - loading more commands from the configured Flask app. - """ - - def __init__(self, help=None): - def set_app_id(ctx, value): - if value is not None: - if os.path.isfile(value): - value = prepare_exec_for_file(value) - elif '.' not in sys.path: - sys.path.insert(0, '.') - ctx.ensure_object(ScriptInfo).app_import_path = value - def set_debug(ctx, value): - ctx.ensure_object(ScriptInfo).debug = value - - click.Group.__init__(self, help=help, params=[ - click.Option(['-a', '--app'], - help='The application to run', - callback=set_app_id, is_eager=True), - click.Option(['--debug/--no-debug'], - help='Enable or disable debug mode.', - default=None, callback=set_debug) - ]) - - -cli = FlaskGroup(help='''\ -This shell command acts as general utility script for Flask applications. - -It loads the application configured (either through the FLASK_APP environment -variable or the --app parameter) and then provides commands either provided -by the application or Flask itself. - -The most useful commands are the "run" and "shell" command. - -Example usage: - - flask --app=hello --debug run -''') - - -@cli.command('run', short_help='Runs a development server.') +@click.command('run', short_help='Runs a development server.') @click.option('--host', '-h', default='127.0.0.1', help='The interface to bind to.') @click.option('--port', '-p', default=5000, @@ -303,7 +321,17 @@ Example usage: @pass_script_info def run_command(info, host, port, reload, debugger, eager_loading, with_threads): - """Runs a local development server for the Flask application.""" + """Runs a local development server for the Flask application. + + This local server is recommended for development purposes only but it + can also be used for simple intranet deployments. By default it will + not support any sort of concurrency at all to simplify debugging. This + can be changed with the --with-threads option which will enable basic + multithreading. + + The reloader and debugger are by default enabled if the debug flag of + Flask is enabled and disabled otherwise. + """ from werkzeug.serving import run_simple if reload is None: reload = info.debug @@ -330,7 +358,7 @@ def run_command(info, host, port, reload, debugger, eager_loading, use_debugger=debugger, threaded=with_threads) -@cli.command('shell', short_help='Runs a shell in the app context.') +@click.command('shell', short_help='Runs a shell in the app context.') def shell_command(): """Runs an interactive Python shell in the context of a given Flask application. The application will populate the default @@ -360,6 +388,21 @@ def make_default_cli(app): return click.Group() +cli = FlaskGroup(help='''\ +This shell command acts as general utility script for Flask applications. + +It loads the application configured (either through the FLASK_APP environment +variable or the --app parameter) and then provides commands either provided +by the application or Flask itself. + +The most useful commands are the "run" and "shell" command. + +Example usage: + + flask --app=hello --debug run +''') + + def main(as_module=False): this_module = __package__ + '.cli' args = sys.argv[1:] @@ -377,8 +420,7 @@ def main(as_module=False): else: name = None - cli.main(args=args, prog_name=name, obj=ScriptInfo(), - auto_envvar_prefix='FLASK') + cli.main(args=args, prog_name=name) if __name__ == '__main__': From 44b0aeb048c1019e3cdd42fd2bdf9c2c8ffb7429 Mon Sep 17 00:00:00 2001 From: Marc Schlaich Date: Tue, 29 Apr 2014 14:34:21 +0200 Subject: [PATCH 0182/2502] Fixed typo. --- flask/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index adca5ca1..2e6c8318 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -290,7 +290,7 @@ Example usage: help='The port to bind to.') @click.option('--reload/--no-reload', default=None, help='Enable or disable the reloader. By default the reloader ' - 'is active is debug is enabled.') + 'is active if debug is enabled.') @click.option('--debugger/--no-debugger', default=None, help='Enable or disable the debugger. By default the debugger ' 'is active if debug is enabled.') From 3569fc24415f8bac7b269ec041bd1f6bc23038ce Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 21:35:51 +0200 Subject: [PATCH 0183/2502] Greatly refactored click integration and documented it a bit more. --- docs/api.rst | 27 ++++++++ docs/cli.rst | 114 ++++++++++++++++++++++++++++++- flask/cli.py | 188 +++++++++++++++++++++++++++++---------------------- setup.py | 2 +- 4 files changed, 249 insertions(+), 82 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 6c9f7414..945082bc 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -753,3 +753,30 @@ Full example:: .. versionadded:: 0.8 The `provide_automatic_options` functionality was added. + +Command Line Interface +---------------------- + +.. currentmodule:: flask.cli + +.. autoclass:: FlaskGroup + :members: + +.. autoclass:: ScriptInfo + :members: + +.. autofunction:: pass_script_info + +.. autofunction:: without_appcontext + +.. autofunction:: script_info_option + + A special decorator that informs a click callback to be passed the + script info object as first argument. This is normally not useful + unless you implement very special commands like the run command which + does not want the application to be loaded yet. This can be combined + with the :func:`without_appcontext` decorator. + +.. autodata:: run_command + +.. autodata:: shell_command diff --git a/docs/cli.rst b/docs/cli.rst index 0ac578e9..171352eb 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -108,7 +108,8 @@ In case you are using factory functions to create your application (see work with them directly. Flask won't be able to figure out how to instanciate your application properly by itself. Because of this reason the recommendation is to create a separate file that instanciates -applications. +applications. This is by far not the only way to make this work. Another +is the :ref:`custom-scripts` support. For instance if you have a factory function that creates an application from a filename you could make a separate file that creates such an @@ -128,3 +129,114 @@ it up:: export FLASK_APP=/path/to/autoapp.py From this point onwards ``flask`` will find your application. + +.. _custom-scripts: + +Custom Scripts +-------------- + +While the most common way is to use the ``flask`` command, you can also +make your own "driver scripts". Since Flask uses click for the scripts +there is no reason you cannot hook these scripts into any click +application. There is one big caveat and that is, that commands +registered to :attr:`Flask.cli` will expect to be (indirectly at least) +launched from a :class:`flask.cli.FlaskGroup` click group. This is +necessary so that the commands know which Flask application they have to +work with. + +To understand why you might want custom scripts you need to understand how +click finds and executes the Flask application. If you use the ``flask`` +script you specify the application to work with on the command line or +environment variable as an import name. This is simple but it has some +limitations. Primarily it does not work with application factory +functions (see :ref:`app-factories`). + +With a custom script you don't have this problem as you can fully +customize how the application will be created. This is very useful if you +write reusable applications that you want to ship to users and they should +be presented with a custom management script. + +If you are used to writing click applications this will look familiar but +at the same time, slightly different because of how commands are loaded. +We won't go into detail now about the differences but if you are curious +you can have a look at the :ref:`script-info-object` section to learn all +about it. + +To explain all of this here an example ``manage.py`` script that manages a +hypothetical wiki application. We will go through the details +afterwards:: + + import click + from flask.cli import FlaskGroup, script_info_option + + def create_wiki_app(info): + from yourwiki import create_app + config = info.data.get('config') or 'wikiconfig.py' + return create_app(config=config) + + @click.group(cls=FlaskGroup, create_app=create_wiki_app) + @script_info_option('--config', script_info_key='config') + def cli(**params): + """This is a management script for the wiki application.""" + + if __name__ == '__main__': + cli() + +That's a lot of code for not much, so let's go through all parts step by +step. + +1. At first we import regular ``click`` as well as the click extensions + from the ``flask.cli`` package. Primarily we are here interested + in the :class:`~flask.cli.FlaskGroup` click group and the + :func:`~flask.cli.script_info_option` decorator. +2. The next thing we do is defining a function that is invoked with the + script info object (:ref:`script-info-object`) from flask and it's + purpose is to fully import and create the application. This can + either directly import an application object or create it (see + :ref:`app-factories`). + + What is ``data.info``? It's a dictionary of arbitrary data on the + script info that can be filled by options or through other means. We + will come back to this later. +3. Next step is to create a :class:`FlaskGroup`. In this case we just + make an empty function with a help doc string that just does nothing + and then pass the ``create_wiki_app`` function as factory function. + + Whenever click now needs to operate on a flask application it will + call that function with the script info and ask for it to be created. +4. In step 2 you could see that the config is passed to the actual + creation function. This config comes from the :func:`script_info_option` + decorator for the main script. It accepts a ``--config`` option and + then stores it in the script info so we can use it to create the + application. +5. All is rounded up by invoking the script. + +.. _script-info-object: + +The Script Info +--------------- + +The Flask script integration might be confusing at first, but it has good +rasons it's done this way. The reason for this is that Flask wants to +both provide custom commands to click as well as not loading your +application unless it has to. The reason for this is added flexibility. + +This way an application can provide custom commands, but even in the +absence of an application the ``flask`` script is still operational on a +basic level. In addition to that does it mean that the individual +commands have the option to not create an instance of the Flask +application unless required. This is very useful as it allows the server +command for instance, the load the application on first request instead of +immediately to give a better debug experience. + +All of this is provided through the :class:`flask.cli.ScriptInfo` object +and some helper utilities around. The basic way it operates is that when +the :class:`flask.cli.FlaskGroup` executes as a script it creates a script +info and keeps it around. From that point onwards modifications on the +script info can be done through click options. To simplify this pattern +the :func:`flask.cli.script_info_option` decorator was added. + +One Flask actually needs the individual Flask application it will invoke +the :meth:`flask.cli.ScriptInfo.load_app` method. This happens when the +server starts, when the shell is launched or when the script looks for an +application provided click command. diff --git a/flask/cli.py b/flask/cli.py index aa385011..a1308e85 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -81,7 +81,7 @@ def prepare_exec_for_file(filename): return '.'.join(module[::-1]) -def locate_app(app_id, debug=None): +def locate_app(app_id): """Attempts to locate the application.""" if ':' in app_id: module, app_obj = app_id.split(':', 1) @@ -98,8 +98,7 @@ def locate_app(app_id, debug=None): if app is None: raise RuntimeError('Failed to find application in module "%s"' % module) - if debug is not None: - app.debug = debug + return app @@ -145,13 +144,24 @@ class ScriptInfo(object): """ def __init__(self, app_import_path=None, debug=None, create_app=None): + #: The application import path self.app_import_path = app_import_path + #: The debug flag. If this is not None, the application will + #: automatically have it's debug flag overridden with this value. self.debug = debug + #: Optionally a function that is passed the script info to create + #: the instance of the application. self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data = {} self._loaded_app = None def load_app(self): - """Loads the Flask app (if not yet loaded) and returns it.""" + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ if self._loaded_app is not None: return self._loaded_app if self.create_app is not None: @@ -159,29 +169,12 @@ class ScriptInfo(object): else: if self.app_import_path is None: _no_such_app() - rv = locate_app(self.app_import_path, self.debug) + rv = locate_app(self.app_import_path) + if self.debug is not None: + rv.debug = self.debug self._loaded_app = rv return rv - def make_wsgi_app(self, use_eager_loading=False): - """Returns a WSGI app that loads the actual application at a later - stage (on first request). This has the advantage over - :meth:`load_app` that if used with a WSGI server, it will allow - the server to intercept errors later during request handling - instead of dying a horrible death. - - If eager loading is disabled the loading will happen immediately. - """ - if self.app_import_path is not None: - def loader(): - return locate_app(self.app_import_path, self.debug) - else: - if self.create_app is None: - _no_such_app() - def loader(): - return self.create_app(self) - return DispatchingApp(loader, use_eager_loading=use_eager_loading) - @contextmanager def conditional_context(self, with_context=True): """Creates an application context or not, depending on the given @@ -189,17 +182,12 @@ class ScriptInfo(object): shortcut for a common operation. """ if with_context: - with self.load_app(self).app_context() as ctx: + with self.load_app().app_context() as ctx: yield ctx else: yield None -#: A special decorator that informs a click callback to be passed the -#: script info object as first argument. This is normally not useful -#: unless you implement very special commands like the run command which -#: does not want the application to be loaded yet. This can be combined -#: with the :func:`without_appcontext` decorator. pass_script_info = click.make_pass_decorator(ScriptInfo) @@ -213,54 +201,65 @@ def without_appcontext(f): return f -class FlaskGroup(click.Group): - """Special subclass of the a regular click group that supports - loading more commands from the configured Flask app. Normally a - developer does not have to interface with this class but there are - some very advanced usecases for which it makes sense to create an - instance of this. +def set_debug_value(ctx, value): + ctx.ensure_object(ScriptInfo).debug = value - :param add_default_options: if this is True the app and debug option - is automatically added. + +def set_app_value(ctx, value): + if value is not None: + if os.path.isfile(value): + value = prepare_exec_for_file(value) + elif '.' not in sys.path: + sys.path.insert(0, '.') + ctx.ensure_object(ScriptInfo).app_import_path = value + + +debug_option = click.Option(['--debug/--no-debug'], + help='Enable or disable debug mode.', + default=None, callback=set_debug_value) + + +app_option = click.Option(['-a', '--app'], + help='The application to run', + callback=set_app_value, is_eager=True) + + +class FlaskGroup(click.Group): + """Special subclass of the a regular click group that supports loading + more commands from the configured Flask app. Normally a developer + does not have to interface with this class but there are some very + advanced usecases for which it makes sense to create an instance of + this. + + For information as of why this is useful see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands wil be added. + :param add_app_option: adds the default ``--app`` option. This gets + automatically disabled if a `create_app` + callback is defined. + :param add_debug_option: adds the default ``--debug`` option. + :param create_app: an optional callback that is passed the script info + and returns the loaded app. """ - def __init__(self, add_default_options=True, - add_default_commands=True, - create_app=None, **extra): - click.Group.__init__(self, **extra) + def __init__(self, add_default_commands=True, add_app_option=None, + add_debug_option=True, create_app=None, **extra): + params = list(extra.pop('params', None) or ()) + if add_app_option is None: + add_app_option = create_app is None + if add_app_option: + params.append(app_option) + if add_debug_option: + params.append(debug_option) + + click.Group.__init__(self, params=params, **extra) self.create_app = create_app - if add_default_options: - self.add_app_option() - self.add_debug_option() + if add_default_commands: self.add_command(run_command) self.add_command(shell_command) - def add_app_option(self): - """Adds an option to the default command that defines an import - path that points to an application. - """ - def set_app_id(ctx, value): - if value is not None: - if os.path.isfile(value): - value = prepare_exec_for_file(value) - elif '.' not in sys.path: - sys.path.insert(0, '.') - ctx.ensure_object(ScriptInfo).app_import_path = value - - self.params.append(click.Option(['-a', '--app'], - help='The application to run', - callback=set_app_id, is_eager=True)) - - def add_debug_option(self): - """Adds an option that controls the debug flag.""" - def set_debug(ctx, value): - ctx.ensure_object(ScriptInfo).debug = value - - self.params.append(click.Option(['--debug/--no-debug'], - help='Enable or disable debug mode.', - default=None, callback=set_debug)) - def get_command(self, ctx, name): info = ctx.ensure_object(ScriptInfo) # Find the command in the application first, if we can find it. @@ -301,6 +300,33 @@ class FlaskGroup(click.Group): return click.Group.main(self, *args, **kwargs) +def script_info_option(*args, **kwargs): + """This decorator works exactly like :func:`click.option` but is eager + by default and stores the value in the :attr:`ScriptInfo.data`. This + is useful to further customize an application factory in very complex + situations. + + :param script_info_key: this is a mandatory keyword argument which + defines under which data key the value should + be stored. + """ + try: + key = kwargs.pop('script_info_key') + except LookupError: + raise TypeError('script_info_key not provided.') + + real_callback = kwargs.get('callback') + def callback(ctx, value): + if real_callback is not None: + value = real_callback(ctx, value) + ctx.ensure_object(ScriptInfo).data[key] = value + return value + + kwargs['callback'] = callback + kwargs.setdefault('is_eager', True) + return click.option(*args, **kwargs) + + @click.command('run', short_help='Runs a development server.') @click.option('--host', '-h', default='127.0.0.1', help='The interface to bind to.') @@ -340,7 +366,7 @@ def run_command(info, host, port, reload, debugger, eager_loading, if eager_loading is None: eager_loading = not reload - app = info.make_wsgi_app(use_eager_loading=eager_loading) + app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) # Extra startup messages. This depends a but on Werkzeug internals to # not double execute when the reloader kicks in. @@ -388,19 +414,21 @@ def make_default_cli(app): return click.Group() -cli = FlaskGroup(help='''\ -This shell command acts as general utility script for Flask applications. +@click.group(cls=FlaskGroup) +def cli(**params): + """ + This shell command acts as general utility script for Flask applications. -It loads the application configured (either through the FLASK_APP environment -variable or the --app parameter) and then provides commands either provided -by the application or Flask itself. + It loads the application configured (either through the FLASK_APP environment + variable or the --app parameter) and then provides commands either provided + by the application or Flask itself. -The most useful commands are the "run" and "shell" command. + The most useful commands are the "run" and "shell" command. -Example usage: + Example usage: - flask --app=hello --debug run -''') + flask --app=hello --debug run + """ def main(as_module=False): diff --git a/setup.py b/setup.py index 196eca4c..1df413b2 100644 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ setup( 'Werkzeug>=0.7', 'Jinja2>=2.4', 'itsdangerous>=0.21', - 'click', + 'click>=0.6', ], classifiers=[ 'Development Status :: 4 - Beta', From 2639a23b8875abb6c1dd2889728920cce5d09632 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 21:51:12 +0200 Subject: [PATCH 0184/2502] Updated setup.py a bit. This fixes #1018 --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index 1df413b2..0a5eb538 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,8 @@ intentions. And before you ask: It's BSD licensed! Flask is Fun ```````````` +Save in a hello.py: + .. code:: python from flask import Flask @@ -23,6 +25,8 @@ Flask is Fun And Easy to Setup ````````````````` +And run it: + .. code:: bash $ pip install Flask From 1b77a3fb11a0819114804e4f88233b2dc215b653 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 22:26:50 +0200 Subject: [PATCH 0185/2502] Made errors for working outside of contexts clearer. --- flask/globals.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/flask/globals.py b/flask/globals.py index fecbfc10..03c2f277 100644 --- a/flask/globals.py +++ b/flask/globals.py @@ -14,24 +14,41 @@ from functools import partial from werkzeug.local import LocalStack, LocalProxy +_request_ctx_err_msg = '''\ +Working outside of request context. + +This typically means that you attempted to use functionality that needed +an active HTTP request. Consult the documentation on testing for +information about how to avoid this problem.\ +''' +_app_ctx_err_msg = '''\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +to interface with the current application object in a way. To solve +this set up an application context with app.app_context(). See the +documentation for more information.\ +''' + + def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: - raise RuntimeError('working outside of request context') + raise RuntimeError(_request_ctx_err_msg) return getattr(top, name) def _lookup_app_object(name): top = _app_ctx_stack.top if top is None: - raise RuntimeError('working outside of application context') + raise RuntimeError(_app_ctx_err_msg) return getattr(top, name) def _find_app(): top = _app_ctx_stack.top if top is None: - raise RuntimeError('working outside of application context') + raise RuntimeError(_app_ctx_err_msg) return top.app From a700cb2a76c0f6037f6ddaa6a519fcba179efd41 Mon Sep 17 00:00:00 2001 From: Julen Ruiz Aizpuru Date: Wed, 7 May 2014 22:32:43 +0200 Subject: [PATCH 0186/2502] Docs: fixes --- docs/cli.rst | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 171352eb..477802eb 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -23,7 +23,7 @@ the same. The way this script works is by providing access to all the commands on your Flask application's :attr:`Flask.cli` instance as well as some built-in commands that are always there. Flask extensions can also -register more commands there if they so desire. +register more commands there if they desire so. For the ``flask`` script to work, an application needs to be discovered. The two most common ways are either an environment variable @@ -106,8 +106,8 @@ Factory Functions In case you are using factory functions to create your application (see :ref:`app-factories`) you will discover that the ``flask`` command cannot work with them directly. Flask won't be able to figure out how to -instanciate your application properly by itself. Because of this reason -the recommendation is to create a separate file that instanciates +instantiate your application properly by itself. Because of this reason +the recommendation is to create a separate file that instantiates applications. This is by far not the only way to make this work. Another is the :ref:`custom-scripts` support. @@ -115,8 +115,7 @@ For instance if you have a factory function that creates an application from a filename you could make a separate file that creates such an application from an environment variable. -For instance this could be a file named ``autoapp.py`` with these -contents:: +This could be a file named ``autoapp.py`` with these contents:: import os from yourapplication import create_app @@ -162,8 +161,8 @@ We won't go into detail now about the differences but if you are curious you can have a look at the :ref:`script-info-object` section to learn all about it. -To explain all of this here an example ``manage.py`` script that manages a -hypothetical wiki application. We will go through the details +To explain all of this, here is an example ``manage.py`` script that +manages a hypothetical wiki application. We will go through the details afterwards:: import click @@ -185,12 +184,12 @@ afterwards:: That's a lot of code for not much, so let's go through all parts step by step. -1. At first we import regular ``click`` as well as the click extensions +1. First we import the ``click`` library as well as the click extensions from the ``flask.cli`` package. Primarily we are here interested in the :class:`~flask.cli.FlaskGroup` click group and the :func:`~flask.cli.script_info_option` decorator. 2. The next thing we do is defining a function that is invoked with the - script info object (:ref:`script-info-object`) from flask and it's + script info object (:ref:`script-info-object`) from Flask and its purpose is to fully import and create the application. This can either directly import an application object or create it (see :ref:`app-factories`). @@ -200,9 +199,9 @@ step. will come back to this later. 3. Next step is to create a :class:`FlaskGroup`. In this case we just make an empty function with a help doc string that just does nothing - and then pass the ``create_wiki_app`` function as factory function. + and then pass the ``create_wiki_app`` function as a factory function. - Whenever click now needs to operate on a flask application it will + Whenever click now needs to operate on a Flask application it will call that function with the script info and ask for it to be created. 4. In step 2 you could see that the config is passed to the actual creation function. This config comes from the :func:`script_info_option` @@ -223,11 +222,11 @@ application unless it has to. The reason for this is added flexibility. This way an application can provide custom commands, but even in the absence of an application the ``flask`` script is still operational on a -basic level. In addition to that does it mean that the individual -commands have the option to not create an instance of the Flask -application unless required. This is very useful as it allows the server -command for instance, the load the application on first request instead of -immediately to give a better debug experience. +basic level. In addition to that it means that the individual commands +have the option to avoid creating an instance of the Flask application +unless required. This is very useful as it allows the server commands for +instance to load the application on a first request instead of +immediately, therefore giving a better debug experience. All of this is provided through the :class:`flask.cli.ScriptInfo` object and some helper utilities around. The basic way it operates is that when @@ -239,4 +238,4 @@ the :func:`flask.cli.script_info_option` decorator was added. One Flask actually needs the individual Flask application it will invoke the :meth:`flask.cli.ScriptInfo.load_app` method. This happens when the server starts, when the shell is launched or when the script looks for an -application provided click command. +application-provided click command. From fa6eded6f572dd4bc23b030f025156cdd1e63305 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 8 May 2014 22:33:58 +0200 Subject: [PATCH 0187/2502] Fixed the cli system failing syntax errors. --- flask/cli.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 5701e9f2..7415bfea 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -261,16 +261,24 @@ class FlaskGroup(click.Group): self.add_command(shell_command) def get_command(self, ctx, name): + # We load built-in commands first as these should always be the + # same no matter what the app does. If the app does want to + # override this it needs to make a custom instance of this group + # and not attach the default commands. + # + # This also means that the script stays functional in case the + # application completely fails. + rv = click.Group.get_command(self, ctx, name) + if rv is not None: + return rv + info = ctx.ensure_object(ScriptInfo) - # Find the command in the application first, if we can find it. - # If the app is not available, we just ignore this silently. try: rv = info.load_app().cli.get_command(ctx, name) if rv is not None: return rv except NoAppException: pass - return click.Group.get_command(self, ctx, name) def list_commands(self, ctx): # The commands available is the list of both the application (if @@ -279,7 +287,11 @@ class FlaskGroup(click.Group): info = ctx.ensure_object(ScriptInfo) try: rv.update(info.load_app().cli.list_commands(ctx)) - except NoAppException: + except Exception: + # Here we intentionally swallow all exceptions as we don't + # want the help page to break if the app does not exist. + # If someone attempts to use the command we try to create + # the app again and this will give us the error. pass return sorted(rv) From 224367fe98a056ffda7452eddd8f7c33fa2214c1 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Fri, 9 May 2014 07:48:08 -0700 Subject: [PATCH 0188/2502] fixed import for python 3 --- flask/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__main__.py b/flask/__main__.py index cbcdebf1..78362293 100644 --- a/flask/__main__.py +++ b/flask/__main__.py @@ -11,5 +11,5 @@ if __name__ == '__main__': - from cli import main + from .cli import main main(as_module=True) From 4a6f932610b3e3b7858017ab1cc4c20bd99ee008 Mon Sep 17 00:00:00 2001 From: lord63 Date: Sun, 11 May 2014 14:08:35 +0800 Subject: [PATCH 0189/2502] fix a small mistake in setup.rst --- docs/tutorial/setup.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 0b76e105..41c22e6e 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -66,7 +66,7 @@ if no such environment key is set. In addition to that you can use the :meth:`~flask.Config.from_object` method on the config object and provide it with an import name of a -module. Flask will the initialize the variable from that module. Note +module. Flask will then initialize the variable from that module. Note that in all cases only variable names that are uppercase are considered. The ``SECRET_KEY`` is needed to keep the client-side sessions secure. From fcec4b140c49ab87ca761e70c4ab98f5417a2744 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Sun, 11 May 2014 13:46:58 +0100 Subject: [PATCH 0190/2502] Fix a tiny spelling error: untilization -> utilization --- docs/python3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/python3.rst b/docs/python3.rst index e2556461..8173dd45 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -19,7 +19,7 @@ In addition to that you need to use the latest and greatest versions of API Stability ------------- -Some of the decisions made in regards to unicode and byte untilization on +Some of the decisions made in regards to unicode and byte utilization on Python 3 make it hard to write low level code. This mainly affects WSGI middlewares and interacting with the WSGI provided information. Werkzeug wraps all that information in high-level helpers but some of those were From 7321a480ea60ee212b06cd6698e1e3ab62b5e7f5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 12 May 2014 02:13:32 +0200 Subject: [PATCH 0191/2502] Simplified click integration a bit --- flask/app.py | 4 ++-- flask/cli.py | 38 ++++++++++++-------------------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/flask/app.py b/flask/app.py index c7196be4..b397f38d 100644 --- a/flask/app.py +++ b/flask/app.py @@ -11,6 +11,7 @@ import os import sys +import click from threading import Lock from datetime import timedelta from itertools import chain @@ -34,7 +35,6 @@ from .templating import DispatchingJinjaLoader, Environment, \ _default_template_ctx_processor from .signals import request_started, request_finished, got_request_exception, \ request_tearing_down, appcontext_tearing_down -from .cli import make_default_cli from ._compat import reraise, string_types, text_type, integer_types # a lock used for logger initialization @@ -544,7 +544,7 @@ class Flask(_PackageBoundObject): #: provided by Flask itself and can be overridden. #: #: This is an instance of a :class:`click.Group` object. - self.cli = make_default_cli(self) + self.cli = click.Group(self) def _get_error_handlers(self): from warnings import warn diff --git a/flask/cli.py b/flask/cli.py index 7415bfea..ec8c5ca5 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -131,12 +131,6 @@ class DispatchingApp(object): return rv(environ, start_response) -def _no_such_app(): - raise NoAppException('Could not locate Flask application. ' - 'You did not provide FLASK_APP or the ' - '--app parameter.') - - class ScriptInfo(object): """Help object to deal with Flask applications. This is usually not necessary to interface with as it's used internally in the dispatching @@ -168,7 +162,9 @@ class ScriptInfo(object): rv = self.create_app(self) else: if self.app_import_path is None: - _no_such_app() + raise NoAppException('Could not locate Flask application. ' + 'You did not provide FLASK_APP or the ' + '--app parameter.') rv = locate_app(self.app_import_path) if self.debug is not None: rv.debug = self.debug @@ -418,29 +414,19 @@ def shell_command(): code.interact(banner=banner, local=app.make_shell_context()) -def make_default_cli(app): - """Creates the default click object for the app itself. Currently - there are no default commands registered because all builtin commands - are registered on the actual cmd object here. - """ - return click.Group() +cli = FlaskGroup(help="""\ +This shell command acts as general utility script for Flask applications. +It loads the application configured (either through the FLASK_APP environment +variable or the --app parameter) and then provides commands either provided +by the application or Flask itself. -@click.group(cls=FlaskGroup) -def cli(**params): - """ - This shell command acts as general utility script for Flask applications. +The most useful commands are the "run" and "shell" command. - It loads the application configured (either through the FLASK_APP environment - variable or the --app parameter) and then provides commands either provided - by the application or Flask itself. +Example usage: - The most useful commands are the "run" and "shell" command. - - Example usage: - - flask --app=hello --debug run - """ + flask --app=hello --debug run +""") def main(as_module=False): From 937ad10275af3e190dcb63df926812a57d1e3e7e Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 12 May 2014 02:16:13 +0200 Subject: [PATCH 0192/2502] Simplified app finding for cli --- flask/cli.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index ec8c5ca5..6d393345 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -39,13 +39,8 @@ def find_best_app(module): matches = [v for k, v in iteritems(module.__dict__) if isinstance(v, Flask)] - if matches: - if len(matches) > 1: - raise NoAppException('More than one possible Flask application ' - 'found in module "%s", none of which are called ' - '"app". Be explicit!' % module.__name__) + if len(matches) == 1: return matches[0] - raise NoAppException('Failed to find application in module "%s". Are ' 'you sure it contains a Flask application? Maybe ' 'you wrapped it in a WSGI middleware or you are ' From 2b8a281c1c73aa31ea81fc4e77c26d741bda87a6 Mon Sep 17 00:00:00 2001 From: lord63 Date: Mon, 12 May 2014 16:01:53 +0800 Subject: [PATCH 0193/2502] fix a small mistake in setup.rst --- docs/tutorial/setup.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 41c22e6e..1fe4ea59 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -4,7 +4,7 @@ Step 2: Application Setup Code ============================== Now that we have the schema in place we can create the application module. -Let's call it flaskr.py. We will place this file inside the flask folder. +Let's call it flaskr.py. We will place this file inside the flaskr folder. We will begin by adding the imports we need and by adding the config section. For small applications, it is possible to drop the configuration directly into the module, and this is what we will be doing here. However From 367a168d867d9b1b36485c98d9c6e5ade1d85686 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Tue, 13 May 2014 22:04:05 +0100 Subject: [PATCH 0194/2502] Fixes #1051 --- docs/cli.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 477802eb..f1d8976d 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -17,7 +17,7 @@ Basic Usage After installation of Flask you will now find a ``flask`` script installed into your virtualenv. If you don't want to install Flask or you have a -special use-case you can also use ``python -mflask`` to accomplish exactly +special use-case you can also use ``python -m flask`` to accomplish exactly the same. The way this script works is by providing access to all the commands on @@ -215,8 +215,8 @@ step. The Script Info --------------- -The Flask script integration might be confusing at first, but it has good -rasons it's done this way. The reason for this is that Flask wants to +The Flask script integration might be confusing at first, but there is a reason +why it's done this way. The reason for this is that Flask wants to both provide custom commands to click as well as not loading your application unless it has to. The reason for this is added flexibility. From 1f5927eee2288b4aaf508af5dc1f148aa2140d91 Mon Sep 17 00:00:00 2001 From: Shalabh Aggarwal Date: Wed, 14 May 2014 09:15:34 +0530 Subject: [PATCH 0195/2502] Fixed a small typo with flask.g docstring --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index b397f38d..d6371c43 100644 --- a/flask/app.py +++ b/flask/app.py @@ -167,7 +167,7 @@ class Flask(_PackageBoundObject): #: #: In Flask 0.9 this property was called `request_globals_class` but it #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the - #: flask.g object is not application context scoped. + #: flask.g object is now application context scoped. #: #: .. versionadded:: 0.10 app_ctx_globals_class = _AppCtxGlobals From 02b02543b34596e576c2e3d78c56bf85600f0c5a Mon Sep 17 00:00:00 2001 From: Wing Date: Sat, 17 May 2014 14:44:34 +0800 Subject: [PATCH 0196/2502] Doc for add check upload file --- docs/patterns/fileuploads.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index 74cdd98b..00f44a93 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -56,7 +56,16 @@ the file and redirects the user to the URL for the uploaded file:: @app.route('/', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': + # check if the post request has the file part + if 'file' not in request.files: + flash('No file part') + return redirect(request.url) file = request.files['file'] + # if user does not select file, browser also + # submit a empty part without filename + if file.filename == '': + flash('No selected file') + return redirect(request.url) if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) From 305ccd5df962eb7d2c9e3d95cfd8eeb280f949e5 Mon Sep 17 00:00:00 2001 From: kekumu Date: Mon, 19 May 2014 01:48:11 -0400 Subject: [PATCH 0197/2502] Update advanced_foreword.rst I think the doc is supposed to read, "Because of that, Flask has a few design choices that some people might find surprising or unorthodox." Otherwise, it could possibly mean, "Because of that, Flask has few design choices that people would find surprising or unorthodox." Or perhaps even less ambiguous, "Because of that, Flask has few surprising or unorthodox design choices." --- docs/advanced_foreword.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced_foreword.rst b/docs/advanced_foreword.rst index 53df8175..9f7a73bd 100644 --- a/docs/advanced_foreword.rst +++ b/docs/advanced_foreword.rst @@ -8,7 +8,7 @@ Thread-Locals in Flask 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, Flask has few design choices that some people might find surprising or +of that, Flask has 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. This approach is convenient, but requires a valid From bd888eaa61cb32b33dbf1e4cb327eff530bb9955 Mon Sep 17 00:00:00 2001 From: Bulat Bochkariov Date: Tue, 20 May 2014 21:54:54 -0700 Subject: [PATCH 0198/2502] Fixed a typo. --- docs/blueprints.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blueprints.rst b/docs/blueprints.rst index 8f83e120..2486b6ec 100644 --- a/docs/blueprints.rst +++ b/docs/blueprints.rst @@ -97,7 +97,7 @@ these:: ' (HEAD, OPTIONS, GET) -> simple_page.show>, simple_page.show>] -The first one is obviously from the application ifself for the static +The first one is obviously from the application itself for the static files. The other two are for the `show` function of the ``simple_page`` blueprint. As you can see, they are also prefixed with the name of the blueprint and separated by a dot (``.``). From fb5d6744889e834f759922e7c95b9f315384b264 Mon Sep 17 00:00:00 2001 From: David Branner Date: Fri, 30 May 2014 12:48:49 -0400 Subject: [PATCH 0199/2502] add `Table` to import in `sqlalchemy.rst` example --- docs/patterns/sqlalchemy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index 07a762d8..fef9b2e9 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -177,7 +177,7 @@ SQL Abstraction Layer If you just want to use the database system (and SQL) abstraction layer you basically only need the engine:: - from sqlalchemy import create_engine, MetaData + from sqlalchemy import create_engine, MetaData, Table engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) metadata = MetaData(bind=engine) From 6b9370a8545475c40f84a02e3d49467fb6a83d2e Mon Sep 17 00:00:00 2001 From: EJ Lee Date: Mon, 2 Jun 2014 21:28:27 +0900 Subject: [PATCH 0200/2502] Update mod_wsgi.rst note configuration changes in apache 2.4 --- docs/deploying/mod_wsgi.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index baac5a1b..dbcfc0f3 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -105,6 +105,10 @@ refuse to run with the above configuration. On a Windows system, eliminate those +Note: There have been some changes in access control configuration for `Apache 2.4`_. + +.. _Apache 2.4: http://httpd.apache.org/docs/trunk/upgrading.html + For more information consult the `mod_wsgi wiki`_. .. _mod_wsgi: http://code.google.com/p/modwsgi/ From 4541910381e5c3836e4c00b480653f058b149f6f Mon Sep 17 00:00:00 2001 From: Jakub Stasiak Date: Wed, 4 Jun 2014 14:47:26 +0100 Subject: [PATCH 0201/2502] Get rid of _tag->closure->_tag reference cycle --- flask/sessions.py | 50 ++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/flask/sessions.py b/flask/sessions.py index 82ba3506..4a7814d7 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -52,36 +52,38 @@ class SessionMixin(object): modified = True +def _tag(value): + if isinstance(value, tuple): + return {' t': [_tag(x) for x in value]} + elif isinstance(value, uuid.UUID): + return {' u': value.hex} + elif isinstance(value, bytes): + return {' b': b64encode(value).decode('ascii')} + elif callable(getattr(value, '__html__', None)): + return {' m': text_type(value.__html__())} + elif isinstance(value, list): + return [_tag(x) for x in value] + elif isinstance(value, datetime): + return {' d': http_date(value)} + elif isinstance(value, dict): + return dict((k, _tag(v)) for k, v in iteritems(value)) + elif isinstance(value, str): + try: + return text_type(value) + except UnicodeError: + raise UnexpectedUnicodeError(u'A byte string with ' + u'non-ASCII data was passed to the session system ' + u'which can only store unicode strings. Consider ' + u'base64 encoding your string (String was %r)' % value) + return value + + class TaggedJSONSerializer(object): """A customized JSON serializer that supports a few extra types that we take for granted when serializing (tuples, markup objects, datetime). """ def dumps(self, value): - def _tag(value): - if isinstance(value, tuple): - return {' t': [_tag(x) for x in value]} - elif isinstance(value, uuid.UUID): - return {' u': value.hex} - elif isinstance(value, bytes): - return {' b': b64encode(value).decode('ascii')} - elif callable(getattr(value, '__html__', None)): - return {' m': text_type(value.__html__())} - elif isinstance(value, list): - return [_tag(x) for x in value] - elif isinstance(value, datetime): - return {' d': http_date(value)} - elif isinstance(value, dict): - return dict((k, _tag(v)) for k, v in iteritems(value)) - elif isinstance(value, str): - try: - return text_type(value) - except UnicodeError: - raise UnexpectedUnicodeError(u'A byte string with ' - u'non-ASCII data was passed to the session system ' - u'which can only store unicode strings. Consider ' - u'base64 encoding your string (String was %r)' % value) - return value return json.dumps(_tag(value), separators=(',', ':')) def loads(self, value): From bf3708609a3a0a2bd03f60e9075ce9e2720156fb Mon Sep 17 00:00:00 2001 From: Shipeng Feng Date: Sun, 15 Jun 2014 12:42:34 +0800 Subject: [PATCH 0202/2502] Fixed typo in _compat.py --- flask/_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/_compat.py b/flask/_compat.py index d4ec9839..4631d02e 100644 --- a/flask/_compat.py +++ b/flask/_compat.py @@ -19,7 +19,7 @@ _identity = lambda x: x if not PY2: text_type = str string_types = (str,) - integer_types = (int, ) + integer_types = (int,) iterkeys = lambda d: iter(d.keys()) itervalues = lambda d: iter(d.values()) From a8fd417b31f27ffc1704de33dde10ac25c4ea11c Mon Sep 17 00:00:00 2001 From: lord63 Date: Mon, 16 Jun 2014 18:39:16 +0800 Subject: [PATCH 0203/2502] Update installation.rst Add descriptions about how leave from the virtual environment. --- docs/installation.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index 163782f2..9169e048 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -81,6 +81,12 @@ If you are a Windows user, the following command is for you:: Either way, you should now be using your virtualenv (notice how the prompt of your shell has changed to show the active environment). +And if you want to go back to the real world, use the following command:: + + $ deactivate + +After doing this, the prompt of your shell should be as familar as before. + Now you can just enter the following command to get Flask activated in your virtualenv:: From c44a6111bc78ace1849748af424ac0ddabbe5f47 Mon Sep 17 00:00:00 2001 From: lord63 Date: Mon, 16 Jun 2014 18:59:33 +0800 Subject: [PATCH 0204/2502] Update installation.rst --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 9169e048..3923350c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -87,7 +87,7 @@ And if you want to go back to the real world, use the following command:: After doing this, the prompt of your shell should be as familar as before. -Now you can just enter the following command to get Flask activated in your +Now, let's move on. Enter the following command to get Flask activated in your virtualenv:: $ pip install Flask From 777e23f81e5c496cd89d5789bdd18624e319a7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wxcaf=C3=A9=20=28Cl=C3=A9ment=20Hertling=29?= Date: Mon, 16 Jun 2014 22:07:20 +0200 Subject: [PATCH 0205/2502] Update mod_wsgi.rst Changes deprecated pkg_add to pkg install, adds documentation for yum --- docs/deploying/mod_wsgi.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index dbcfc0f3..3c859910 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -29,12 +29,19 @@ follows: # apt-get install libapache2-mod-wsgi +If you are using a yum based distribution (Fedora, OpenSUSE, etc..) you +can install it as follows: + +.. sourcecode:: text + + # yum install mod_wsgi + On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by using pkg_add: .. sourcecode:: text - # pkg_add -r mod_wsgi + # pkg install ap22-mod_wsgi2 If you are using pkgsrc you can install `mod_wsgi` by compiling the `www/ap2-wsgi` package. From 171253a0ebd1262c6e8a03fb682ad93fb523aad8 Mon Sep 17 00:00:00 2001 From: Mark Slater Date: Sun, 29 Jun 2014 15:48:52 -0700 Subject: [PATCH 0206/2502] Update dbcon.rst "Teared" should be "torn", I think? --- docs/tutorial/dbcon.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst index 0e3f7de5..aa6d8bae 100644 --- a/docs/tutorial/dbcon.rst +++ b/docs/tutorial/dbcon.rst @@ -51,7 +51,7 @@ decorator. It's executed every time the application context tears down:: Functions marked with :meth:`~flask.Flask.teardown_appcontext` are called every time the app context tears down. So what does this mean? Essentially the app context is created before the request comes in and is -destroyed (teared down) whenever the request finishes. A teardown can +destroyed (torn down) whenever the request finishes. A teardown can happen because of two reasons: either everything went well (the error parameter will be `None`) or an exception happened in which case the error is passed to the teardown function. From 35282a881eeb11be0e3b4e39ce3e1b6671eb2055 Mon Sep 17 00:00:00 2001 From: Mark Slater Date: Sun, 29 Jun 2014 15:51:03 -0700 Subject: [PATCH 0207/2502] Update dbinit.rst "require" -> "requires" --- docs/tutorial/dbinit.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 10404b3a..e3dc60fb 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -16,7 +16,7 @@ Such a schema can be created by piping the `schema.sql` file into the The downside of this is that it requires the sqlite3 command to be installed which is not necessarily the case on every system. This also -require that we provide the path to the database which can introduce +requires that we provide the path to the database which can introduce errors. It's a good idea to add a function that initializes the database for you to the application. From 2d992e7f135f6e3aa521ad210fc0febd5b93d30f Mon Sep 17 00:00:00 2001 From: root-11 Date: Tue, 1 Jul 2014 15:57:50 +0100 Subject: [PATCH 0208/2502] Update schema.rst The error is that sqlite3 needs escaping on the database column 'text' in the line: text text not null so that it becomes: 'text' text not null The example on mitsuhiko/flask is correct and helped me detecting the mistake. --- docs/tutorial/schema.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/schema.rst b/docs/tutorial/schema.rst index f8455037..246baccd 100644 --- a/docs/tutorial/schema.rst +++ b/docs/tutorial/schema.rst @@ -14,7 +14,7 @@ named `schema.sql` in the just created `flaskr` folder: create table entries ( id integer primary key autoincrement, title text not null, - text text not null + 'text' text not null ); This schema consists of a single table called `entries` and each row in From eb0c8a6beea766812fb94712c27ed6e1e99d6249 Mon Sep 17 00:00:00 2001 From: Suraj Patil Date: Thu, 3 Jul 2014 18:19:09 +0530 Subject: [PATCH 0209/2502] The example does not execute With the inclusion of the app.run we now are able to execute the program, it would be great if we'd have even more detailed docs available for the examples --- examples/blueprintexample/blueprintexample.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/blueprintexample/blueprintexample.py b/examples/blueprintexample/blueprintexample.py index 925f4845..78ee3a5b 100644 --- a/examples/blueprintexample/blueprintexample.py +++ b/examples/blueprintexample/blueprintexample.py @@ -5,3 +5,6 @@ app = Flask(__name__) app.register_blueprint(simple_page) # Blueprint can be registered many times app.register_blueprint(simple_page, url_prefix='/pages') + +if __name__=='__main__': + app.run() From f5b15c2a35933167c516da0c13290375c58c5509 Mon Sep 17 00:00:00 2001 From: Jihyeok Seo Date: Sun, 6 Jul 2014 00:32:31 +0900 Subject: [PATCH 0210/2502] Fix typo in cli docs --- docs/cli.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli.rst b/docs/cli.rst index f1d8976d..6eb6cb9c 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -194,7 +194,7 @@ step. either directly import an application object or create it (see :ref:`app-factories`). - What is ``data.info``? It's a dictionary of arbitrary data on the + What is ``info.data``? It's a dictionary of arbitrary data on the script info that can be filled by options or through other means. We will come back to this later. 3. Next step is to create a :class:`FlaskGroup`. In this case we just From add59a91cf45b567397db134d3e4ed4ce3ecd6da Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Mon, 7 Jul 2014 21:17:20 +0900 Subject: [PATCH 0211/2502] Stop recommending Python 2.6 --- docs/advanced_foreword.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced_foreword.rst b/docs/advanced_foreword.rst index 9f7a73bd..22e333f8 100644 --- a/docs/advanced_foreword.rst +++ b/docs/advanced_foreword.rst @@ -57,7 +57,7 @@ partially caused by changes in the language that went unreviewed for too long, partially also because we have not quite worked out how the lower- level API should change to account for the Unicode differences in Python 3. -We strongly recommend using Python 2.6 and 2.7 with activated Python 3 +We strongly recommend using Python 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 From 931be87475634c79deae584b7fa8d2bd6d6c55c0 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Thu, 10 Jul 2014 23:01:32 +0900 Subject: [PATCH 0212/2502] Update python3.rst Fixes #1101 --- docs/python3.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/python3.rst b/docs/python3.rst index 8173dd45..13cc8455 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -40,12 +40,11 @@ probably hard to search for on the internet if they are Python 3 specific. Small Ecosystem --------------- -The majority of the Flask extensions, all of the documentation and the -vast majority of the PyPI provided libraries do not support Python 3 yet. -Even if you start your project with knowing that all you will need is -supported by Python 3 you don't know what happens six months from now. If -you are adventurous you can start porting libraries on your own, but that -is nothing for the faint of heart. +Some Flask extensions, documentation and PyPI provided libraries do not +support Python 3 yet. Even if you start your project with knowing that +all you will need is supported by Python 3 you don't know what happens six +months from now. If you are adventurous you can start porting libraries +on your own, but that is nothing for the faint of heart. Recommendations --------------- From 92191fb9d9f9d68811be81977faf108d9f1313f4 Mon Sep 17 00:00:00 2001 From: lord63 Date: Wed, 9 Jul 2014 13:22:27 +0800 Subject: [PATCH 0213/2502] Fix a typo in templating.rst --- docs/templating.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/templating.rst b/docs/templating.rst index bf672672..8b7caf44 100644 --- a/docs/templating.rst +++ b/docs/templating.rst @@ -119,7 +119,7 @@ Controlling Autoescaping ------------------------ Autoescaping is the concept of automatically escaping special characters -of you. Special characters in the sense of HTML (or XML, and thus XHTML) +for you. Special characters in the sense of HTML (or XML, and thus XHTML) are ``&``, ``>``, ``<``, ``"`` as well as ``'``. Because these characters carry specific meanings in documents on their own you have to replace them by so called "entities" if you want to use them for text. Not doing so From 3e9f074d05c732c65cad104235807ed662a7df16 Mon Sep 17 00:00:00 2001 From: lord63 Date: Wed, 9 Jul 2014 14:22:30 +0800 Subject: [PATCH 0214/2502] Update becomingbig.rst --- docs/becomingbig.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/becomingbig.rst b/docs/becomingbig.rst index 62f456bd..8b0a2743 100644 --- a/docs/becomingbig.rst +++ b/docs/becomingbig.rst @@ -12,7 +12,7 @@ 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 +written to be read; it's documentation is 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. From de5bb759e3b5f686769c0803541a555475b3ef5f Mon Sep 17 00:00:00 2001 From: lord63 Date: Wed, 9 Jul 2014 14:45:55 +0800 Subject: [PATCH 0215/2502] Fix typos in shell.rst --- docs/shell.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/shell.rst b/docs/shell.rst index f4522c1a..55a428c7 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -94,10 +94,10 @@ Further Improving the Shell Experience -------------------------------------- If you like the idea of experimenting in a shell, create yourself a module -with stuff you want to star import into your interactive session. There +with stuff you want to firstly import into your interactive session. There you could also define some more helper methods for common things such as initializing the database, dropping tables etc. -Just put them into a module (like `shelltools` and import from there): +Just put them into a module (like `shelltools`) and import from there: >>> from shelltools import * From 7777b06809a4fd948e86f5ce8681c8f401ff37ee Mon Sep 17 00:00:00 2001 From: lord63 Date: Thu, 10 Jul 2014 12:04:48 +0800 Subject: [PATCH 0216/2502] Fix a typo in config.rst --- docs/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index d0e1bcdb..e5b2369b 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -7,7 +7,7 @@ Configuration Handling Applications need some kind of configuration. There are different settings you might want to change depending on the application environment like -toggling the debug mode, setting the secret key, and other such +toggling the debug mode, setting the secret key, and other such as environment-specific things. The way Flask is designed usually requires the configuration to be From 02e7bec21cec2f32d954e178a1a88f8dd6aaa81a Mon Sep 17 00:00:00 2001 From: lord63 Date: Fri, 11 Jul 2014 23:23:01 +0800 Subject: [PATCH 0217/2502] Fix typos in config.rst --- docs/config.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index e5b2369b..6bece190 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -10,7 +10,7 @@ you might want to change depending on the application environment like toggling the debug mode, setting the secret key, and other such as environment-specific things. -The way Flask is designed usually requires the configuration to be +The way Flask designed usually requires the configuration to be available when the application starts up. You can hardcode the configuration in the code, which for many small applications is not actually that bad, but there are better ways. @@ -194,7 +194,7 @@ The following configuration values are used internally by Flask: browsers will not allow cross-subdomain cookies to be set on a server name without dots in it. So if your server name is ``'localhost'`` you will not be able to set a cookie for - ``'localhost'`` and every subdomain of it. Please chose a different + ``'localhost'`` and every subdomain of it. Please choose a different server name in that case, like ``'myapplication.local'`` and add this name + the subdomains you want to use into your host config or setup a local `bind`_. @@ -249,7 +249,7 @@ So a common pattern is this:: This first loads the configuration from the `yourapplication.default_settings` module and then overrides the values with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS` -environment variable points to. This environment variable can be set on +environment variable pointing to. This environment variable can be set on Linux or OS X with the export command in the shell before starting the server:: @@ -287,7 +287,7 @@ a little harder. There is no single 100% solution for this problem in general, but there are a couple of things you can keep in mind to improve that experience: -1. create your application in a function and register blueprints on it. +1. Create your application in a function and register blueprints on it. That way you can create multiple instances of your application with different configurations attached which makes unittesting a lot easier. You can use this to pass in configuration as needed. @@ -348,10 +348,10 @@ To enable such a config you just have to call into There are many different ways and it's up to you how you want to manage your configuration files. However here a list of good recommendations: -- keep a default configuration in version control. Either populate the +- Keep a default configuration in version control. Either populate the config with this default configuration or import it in your own configuration files before overriding values. -- use an environment variable to switch between the configurations. +- Use an environment variable to switch between the configurations. This can be done from outside the Python interpreter and makes development and deployment much easier because you can quickly and easily switch between different configs without having to touch the From c8ce5326b4d30f29aaadbb3b81bdcb1cec6b43e8 Mon Sep 17 00:00:00 2001 From: Miles Richardson Date: Tue, 15 Jul 2014 15:19:00 +0800 Subject: [PATCH 0218/2502] fixed typo -- missing "are" in docs/signals.rst --- docs/signals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/signals.rst b/docs/signals.rst index 46342033..c32a5ca2 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -148,7 +148,7 @@ signal subscribers:: model_saved.send(self) Try to always pick a good sender. If you have a class that is emitting a -signal, pass `self` as sender. If you emitting a signal from a random +signal, pass `self` as sender. If you are emitting a signal from a random function, you can pass ``current_app._get_current_object()`` as sender. .. admonition:: Passing Proxies as Senders From 22219f51e5a15836657bc5553fe90dac2054caef Mon Sep 17 00:00:00 2001 From: Nico Revin Date: Thu, 24 Jul 2014 19:03:56 +0400 Subject: [PATCH 0219/2502] Update doclinks Fix redirects and broken links --- docs/config.rst | 4 ++-- docs/deploying/cgi.rst | 4 ++-- docs/deploying/fastcgi.rst | 6 +++--- docs/deploying/mod_wsgi.rst | 4 ++-- docs/deploying/uwsgi.rst | 2 +- docs/extensions.rst | 2 +- docs/htmlfaq.rst | 2 +- docs/installation.rst | 4 ++-- docs/patterns/caching.rst | 4 ++-- docs/patterns/fabric.rst | 4 ++-- docs/patterns/jquery.rst | 2 +- docs/patterns/sqlalchemy.rst | 6 +++--- docs/patterns/wtforms.rst | 4 ++-- docs/quickstart.rst | 4 ++-- docs/testing.rst | 36 ++++++++++++++++++------------------ docs/tutorial/dbcon.rst | 2 +- docs/tutorial/index.rst | 2 +- docs/upgrading.rst | 4 ++-- 18 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index d0e1bcdb..dfcead34 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -199,7 +199,7 @@ The following configuration values are used internally by Flask: this name + the subdomains you want to use into your host config or setup a local `bind`_. -.. _bind: https://www.isc.org/software/bind +.. _bind: https://www.isc.org/downloads/bind/ .. versionadded:: 0.4 ``LOGGER_NAME`` @@ -363,7 +363,7 @@ your configuration files. However here a list of good recommendations: details about how to do that, head over to the :ref:`fabric-deployment` pattern. -.. _fabric: http://fabfile.org/ +.. _fabric: http://www.fabfile.org/ .. _instance-folders: diff --git a/docs/deploying/cgi.rst b/docs/deploying/cgi.rst index 3225bc58..9675d673 100644 --- a/docs/deploying/cgi.rst +++ b/docs/deploying/cgi.rst @@ -51,11 +51,11 @@ your app to be available, works too but the `ScriptAlias` directive won't work in that case: .. sourcecode:: apache - + RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L] For more information consult the documentation of your webserver. -.. _App Engine: http://code.google.com/appengine/ +.. _App Engine: https://developers.google.com/appengine/ diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst index b4f01d57..a0d6d076 100644 --- a/docs/deploying/fastcgi.rst +++ b/docs/deploying/fastcgi.rst @@ -159,7 +159,7 @@ work in the URL root you have to work around a lighttpd bug with the Make sure to apply it only if you are mounting the application the URL root. Also, see the Lighty docs for more information on `FastCGI and Python -`_ (note that +`_ (note that explicitly passing a socket to run() is no longer necessary). Configuring nginx @@ -236,5 +236,5 @@ python path. Common problems are: .. _nginx: http://nginx.org/ .. _lighttpd: http://www.lighttpd.net/ -.. _cherokee: http://www.cherokee-project.com/ -.. _flup: http://trac.saddi.com/flup +.. _cherokee: http://cherokee-project.com/ +.. _flup: https://pypi.python.org/pypi/flup diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index 3c859910..e91aedc9 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -98,7 +98,7 @@ execute the application under a different user for security reasons: -Note: WSGIDaemonProcess isn't implemented in Windows and Apache will +Note: WSGIDaemonProcess isn't implemented in Windows and Apache will refuse to run with the above configuration. On a Windows system, eliminate those lines: .. sourcecode:: apache @@ -121,7 +121,7 @@ For more information consult the `mod_wsgi wiki`_. .. _mod_wsgi: http://code.google.com/p/modwsgi/ .. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide .. _virtual python: https://pypi.python.org/pypi/virtualenv -.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/ +.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/w/list Troubleshooting --------------- diff --git a/docs/deploying/uwsgi.rst b/docs/deploying/uwsgi.rst index b05fdeec..25ed8008 100644 --- a/docs/deploying/uwsgi.rst +++ b/docs/deploying/uwsgi.rst @@ -63,5 +63,5 @@ it the WSGI `SCRIPT_NAME` or set the uwsgi modifier to make use of it:: .. _nginx: http://nginx.org/ .. _lighttpd: http://www.lighttpd.net/ -.. _cherokee: http://www.cherokee-project.com/ +.. _cherokee: http://cherokee-project.com/ .. _uwsgi: http://projects.unbit.it/uwsgi/ diff --git a/docs/extensions.rst b/docs/extensions.rst index 53dca56e..4f80f1e6 100644 --- a/docs/extensions.rst +++ b/docs/extensions.rst @@ -45,4 +45,4 @@ Once the ``flaskext_compat`` module is activated the :data:`flask.ext` will exist and you can start importing from there. .. _Flask Extension Registry: http://flask.pocoo.org/extensions/ -.. _flaskext_compat.py: https://github.com/mitsuhiko/flask/raw/master/scripts/flaskext_compat.py +.. _flaskext_compat.py: https://raw.githubusercontent.com/mitsuhiko/flask/master/scripts/flaskext_compat.py diff --git a/docs/htmlfaq.rst b/docs/htmlfaq.rst index 434bb656..fdf29634 100644 --- a/docs/htmlfaq.rst +++ b/docs/htmlfaq.rst @@ -186,7 +186,7 @@ Many other features have been added, as well. A good guide to new features in HTML5 is Mark Pilgrim's soon-to-be-published book, `Dive Into HTML5`_. Not all of them are supported in browsers yet, however, so use caution. -.. _Dive Into HTML5: http://www.diveintohtml5.info/ +.. _Dive Into HTML5: http://diveintohtml5.info/ What should be used? -------------------- diff --git a/docs/installation.rst b/docs/installation.rst index 3923350c..5bf668af 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -180,8 +180,8 @@ check that you can now just type ``python`` to bring up the interpreter. Finally, to install `virtualenv`_, you can simply run:: > pip install virtualenv - + Then you can be off on your way following the installation instructions above. -.. _get-pip.py: https://raw.github.com/pypa/pip/master/contrib/get-pip.py +.. _get-pip.py: https://raw.githubusercontent.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/caching.rst b/docs/patterns/caching.rst index a0633cf9..97e0f35d 100644 --- a/docs/patterns/caching.rst +++ b/docs/patterns/caching.rst @@ -27,7 +27,7 @@ cache that keeps the item stored in the memory of the Python interpreter:: cache = SimpleCache() If you want to use memcached, make sure to have one of the memcache modules -supported (you get them from `PyPI `_) and a +supported (you get them from `PyPI `_) and a memcached server running somewhere. This is how you connect to such an memcached server then:: @@ -44,7 +44,7 @@ Using a Cache ------------- Now how can one use such a cache? There are two very important -operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and +operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and :meth:`~werkzeug.contrib.cache.BaseCache.set`. This is how to use them: To get an item from the cache call diff --git a/docs/patterns/fabric.rst b/docs/patterns/fabric.rst index b02ad277..e915103b 100644 --- a/docs/patterns/fabric.rst +++ b/docs/patterns/fabric.rst @@ -186,11 +186,11 @@ deployment actually fun: out the latest version on the server and then install. That way you can also easily go back to older versions. - hook in testing functionality so that you can deploy to an external - server and run the testsuite. + server and run the testsuite. Working with Fabric is fun and you will notice that it's quite magical to type ``fab deploy`` and see your application being deployed automatically to one or more remote servers. -.. _Fabric: http://fabfile.org/ +.. _Fabric: http://www.fabfile.org/ diff --git a/docs/patterns/jquery.rst b/docs/patterns/jquery.rst index bb1b4c06..913bcedf 100644 --- a/docs/patterns/jquery.rst +++ b/docs/patterns/jquery.rst @@ -164,5 +164,5 @@ explanation of the little bit of code above: If you don't get the whole picture, download the `sourcecode for this example -`_ +`_ from github. diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index 29a0d9f9..d34f39be 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -110,7 +110,7 @@ Querying is simple as well: .. _SQLAlchemy: http://www.sqlalchemy.org/ .. _declarative: - http://www.sqlalchemy.org/docs/orm/extensions/declarative.html + http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative.html Manual Object Relational Mapping -------------------------------- @@ -186,7 +186,7 @@ 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 @@ -215,4 +215,4 @@ You can also pass strings of SQL statements to the (1, u'admin', u'admin@localhost') For more information about SQLAlchemy, head over to the -`website `_. +`website `_. diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst index 8017aea5..49bdbd43 100644 --- a/docs/patterns/wtforms.rst +++ b/docs/patterns/wtforms.rst @@ -122,5 +122,5 @@ takes advantage of the `_formhelpers.html` template: For more information about WTForms, head over to the `WTForms website`_. -.. _WTForms: http://wtforms.simplecodes.com/ -.. _WTForms website: http://wtforms.simplecodes.com/ +.. _WTForms: http://wtforms.readthedocs.org/ +.. _WTForms website: http://wtforms.readthedocs.org/ diff --git a/docs/quickstart.rst b/docs/quickstart.rst index b39b2a3c..accb942f 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -41,7 +41,7 @@ should see your hello world greeting. So what did that code do? 1. First we imported the :class:`~flask.Flask` class. An instance of this - class will be our WSGI application. + class will be our WSGI application. 2. Next we create an instance of this class. The first argument is the name of the application's module or package. If you are using a single module (as in this example), you should use `__name__` because depending on if it's @@ -878,7 +878,7 @@ Ready to deploy your new Flask app? To wrap up the quickstart, you can immediately deploy to a hosted platform, all of which offer a free plan for small projects: -- `Deploying Flask on Heroku `_ +- `Deploying Flask on Heroku `_ - `Deploying WSGI on dotCloud `_ with `Flask-specific notes `_ diff --git a/docs/testing.rst b/docs/testing.rst index 95b2021a..de5c9143 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -11,7 +11,7 @@ improve existing code and developers of untested applications tend to become pretty paranoid. If an application has automated tests, you can safely make changes and instantly know if anything breaks. -Flask provides a way to test your application by exposing the Werkzeug +Flask provides a way to test your application by exposing the Werkzeug test :class:`~werkzeug.test.Client` and handling the context locals for you. You can then use that with your favourite testing solution. In this documentation we will use the :mod:`unittest` package that comes pre-installed with Python. @@ -19,17 +19,17 @@ we will use the :mod:`unittest` package that comes pre-installed with Python. The Application --------------- -First, we need an application to test; we will use the application from -the :ref:`tutorial`. If you don't have that application yet, get the +First, we need an application to test; we will use the application from +the :ref:`tutorial`. If you don't have that application yet, get the sources from `the examples`_. .. _the examples: - http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ + https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ The Testing Skeleton -------------------- -In order to test the application, we add a second module +In order to test the application, we add a second module (`flaskr_tests.py`) and create a unittest skeleton there:: import os @@ -55,15 +55,15 @@ In order to test the application, we add a second module The code in the :meth:`~unittest.TestCase.setUp` method creates a new test client and initializes a new database. This function is called before -each individual test function is run. To delete the database after the +each individual test function is run. To delete the database after the test, we close the file and remove it from the filesystem in the :meth:`~unittest.TestCase.tearDown` method. Additionally during setup the ``TESTING`` config flag is activated. What it does is disabling the error catching during request handling so that you get better error reports when performing test requests against the application. -This test client will give us a simple interface to the application. We can -trigger test requests to the application, and the client will also keep track +This test client will give us a simple interface to the application. We can +trigger test requests to the application, and the client will also keep track of cookies for us. Because SQLite3 is filesystem-based we can easily use the tempfile module @@ -89,8 +89,8 @@ with an exception. The First Test -------------- -Now it's time to start testing the functionality of the application. -Let's check that the application shows "No entries here so far" if we +Now it's time to start testing the functionality of the application. +Let's check that the application shows "No entries here so far" if we access the root of the application (``/``). To do this, we add a new test method to our class, like this:: @@ -109,13 +109,13 @@ test method to our class, like this:: rv = self.app.get('/') assert 'No entries here so far' in rv.data -Notice that our test functions begin with the word `test`; this allows -:mod:`unittest` to automatically identify the method as a test to run. +Notice that our test functions begin with the word `test`; this allows +:mod:`unittest` to automatically identify the method as a test to run. -By using `self.app.get` we can send an HTTP `GET` request to the application with -the given path. The return value will be a :class:`~flask.Flask.response_class` object. +By using `self.app.get` we can send an HTTP `GET` request to the application with +the given path. The return value will be a :class:`~flask.Flask.response_class` object. We can now use the :attr:`~werkzeug.wrappers.BaseResponse.data` attribute to inspect -the return value (as string) from the application. In this case, we ensure that +the return value (as string) from the application. In this case, we ensure that ``'No entries here so far'`` is part of the output. Run it again and you should see one passing test:: @@ -132,8 +132,8 @@ Logging In and Out The majority of the functionality of our application is only available for the administrative user, so we need a way to log our test client in and out -of the application. To do this, we fire some requests to the login and logout -pages with the required form data (username and password). And because the +of the application. To do this, we fire some requests to the login and logout +pages with the required form data (username and password). And because the login and logout pages redirect, we tell the client to `follow_redirects`. Add the following two methods to your `FlaskrTestCase` class:: @@ -194,7 +194,7 @@ suite. .. _MiniTwit Example: - http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/ + https://github.com/mitsuhiko/flask/tree/master/examples/minitwit/ Other Testing Tricks diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst index aa6d8bae..00f6c32e 100644 --- a/docs/tutorial/dbcon.rst +++ b/docs/tutorial/dbcon.rst @@ -75,4 +75,4 @@ Continue to :ref:`tutorial-dbinit`. larger `, it's a good idea not to. .. _example source: - http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ + https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index da37cf7a..beb22709 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -15,7 +15,7 @@ If you want the full sourcecode in advance or for comparison, check out the `example source`_. .. _example source: - http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ + https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ .. toctree:: :maxdepth: 2 diff --git a/docs/upgrading.rst b/docs/upgrading.rst index ccfa82ad..5b2d6f85 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -64,7 +64,7 @@ If you maintain an extension that was using :data:`~flask._request_ctx_stack` before, please consider changing to :data:`~flask._app_ctx_stack` if it makes sense for your extension. For instance, the app context stack makes sense for extensions which connect to databases. Using the app context stack instead of -the request context stack will make extensions more readily handle use cases +the request context stack will make extensions more readily handle use cases outside of requests. Version 0.8 @@ -115,7 +115,7 @@ good. To apply the upgrade script do the following: 1. Download the script: `flask-07-upgrade.py - `_ + `_ 2. Run it in the directory of your application:: python flask-07-upgrade.py > patchfile.diff From e429e47a967e6846fa6ef723364dd25cc9aea37b Mon Sep 17 00:00:00 2001 From: lord63 Date: Sat, 26 Jul 2014 20:15:36 +0800 Subject: [PATCH 0220/2502] Correct the mistakes. --- docs/config.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 6bece190..660f6ca5 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -7,10 +7,10 @@ Configuration Handling Applications need some kind of configuration. There are different settings you might want to change depending on the application environment like -toggling the debug mode, setting the secret key, and other such as +toggling the debug mode, setting the secret key, and other such environment-specific things. -The way Flask designed usually requires the configuration to be +The way Flask is designed usually requires the configuration to be available when the application starts up. You can hardcode the configuration in the code, which for many small applications is not actually that bad, but there are better ways. @@ -249,7 +249,7 @@ So a common pattern is this:: This first loads the configuration from the `yourapplication.default_settings` module and then overrides the values with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS` -environment variable pointing to. This environment variable can be set on +environment variable points to. This environment variable can be set on Linux or OS X with the export command in the shell before starting the server:: From efdf2e12125f52f02a68e20b6df1814635d2c005 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sat, 26 Jul 2014 21:47:41 +0900 Subject: [PATCH 0221/2502] Update python3.rst --- docs/python3.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/python3.rst b/docs/python3.rst index 13cc8455..a06a270f 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -33,18 +33,20 @@ guarantee that this won't happen on Python 3. Few Users --------- -Python 3 currently has less than 1% of the users of Python 2 going by PyPI -download stats. As a result many of the problems you will encounter are +Although moving to Python 3 should be done someday, most people still uses +Python 2 for now. As a result many of the problems you will encounter are probably hard to search for on the internet if they are Python 3 specific. Small Ecosystem --------------- Some Flask extensions, documentation and PyPI provided libraries do not -support Python 3 yet. Even if you start your project with knowing that -all you will need is supported by Python 3 you don't know what happens six -months from now. If you are adventurous you can start porting libraries -on your own, but that is nothing for the faint of heart. +support Python 3 yet. + +Even if you start your project with knowing that all you will need is +supported by Python 3 you don't know what happens six months from now. +But if you are familiar Python 3 and Flask extension, you can start porting +libraries on your own. Recommendations --------------- From 29768ce45e6e46fd830d11f17250250fd8c5c5fb Mon Sep 17 00:00:00 2001 From: lord63 Date: Sat, 26 Jul 2014 22:43:52 +0800 Subject: [PATCH 0222/2502] Correct the mistake again. --- docs/shell.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/shell.rst b/docs/shell.rst index 55a428c7..f622be9d 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -94,7 +94,7 @@ Further Improving the Shell Experience -------------------------------------- If you like the idea of experimenting in a shell, create yourself a module -with stuff you want to firstly import into your interactive session. There +with stuff you want to star import into your interactive session. There you could also define some more helper methods for common things such as initializing the database, dropping tables etc. From a61347b902961d36d932bab56794fc39fab0d210 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sun, 27 Jul 2014 02:47:33 +0900 Subject: [PATCH 0223/2502] Update python3.rst --- docs/python3.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/python3.rst b/docs/python3.rst index a06a270f..34fec4e9 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -33,7 +33,7 @@ guarantee that this won't happen on Python 3. Few Users --------- -Although moving to Python 3 should be done someday, most people still uses +Although moving to Python 3 should be done someday, most people still use Python 2 for now. As a result many of the problems you will encounter are probably hard to search for on the internet if they are Python 3 specific. @@ -45,8 +45,8 @@ support Python 3 yet. Even if you start your project with knowing that all you will need is supported by Python 3 you don't know what happens six months from now. -But if you are familiar Python 3 and Flask extension, you can start porting -libraries on your own. +But if you are familiar with Python 3 and Flask extension, you can start +porting libraries on your own. Recommendations --------------- From aa40b1731e09e839502994b3b0c22d3f2361d045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sun, 27 Jul 2014 11:19:52 +0200 Subject: [PATCH 0224/2502] Add Config.from_mapping --- CHANGES | 1 + flask/config.py | 22 ++++++++++++++++++++++ flask/testsuite/config.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/CHANGES b/CHANGES index 93364231..ec031c53 100644 --- a/CHANGES +++ b/CHANGES @@ -36,6 +36,7 @@ Version 1.0 for an extension author to create exceptions that will by default result in the HTTP error of their choosing, but may be caught with a custom error handler if desired. +- Added :meth:`flask.Config.from_mapping`. Version 0.10.2 -------------- diff --git a/flask/config.py b/flask/config.py index dfc2f6b3..6119f02b 100644 --- a/flask/config.py +++ b/flask/config.py @@ -193,6 +193,28 @@ class Config(dict): self[key] = obj[key] return True + def from_mapping(self, *mapping, **kwargs): + """Updates the config like :meth:`update` ignoring items with non-upper + keys. + + .. versionadded:: 1.0 + """ + mappings = [] + if len(mapping) == 1: + if hasattr(mapping[0], 'items'): + mappings.append(mapping[0].items()) + else: + mappings.append(mapping[0]) + elif len(mapping) > 1: + raise TypeError( + 'expected at most 1 positional argument, got %d' % len(mapping) + ) + mappings.append(kwargs.items()) + for mapping in mappings: + for (key, value) in mapping: + if key.isupper(): + self[key] = value + def get_namespace(self, namespace, lowercase=True): """Returns a dictionary containing a subset of configuration options that match the specified namespace/prefix. Example usage:: diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py index d0542200..4772fa77 100644 --- a/flask/testsuite/config.py +++ b/flask/testsuite/config.py @@ -47,6 +47,34 @@ class ConfigTestCase(FlaskTestCase): app.config.from_json(os.path.join(current_dir, 'static', 'config.json')) self.common_object_test(app) + def test_config_from_mapping(self): + app = flask.Flask(__name__) + app.config.from_mapping({ + 'SECRET_KEY': 'devkey', + 'TEST_KEY': 'foo' + }) + self.common_object_test(app) + + app = flask.Flask(__name__) + app.config.from_mapping([ + ('SECRET_KEY', 'devkey'), + ('TEST_KEY', 'foo') + ]) + self.common_object_test(app) + + app = flask.Flask(__name__) + app.config.from_mapping( + SECRET_KEY='devkey', + TEST_KEY='foo' + ) + self.common_object_test(app) + + app = flask.Flask(__name__) + with self.assert_raises(TypeError): + app.config.from_mapping( + {}, {} + ) + def test_config_from_class(self): class Base(object): TEST_KEY = 'foo' From 486c1089b0ce968b74e81649cdceb0e158157036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sun, 27 Jul 2014 13:21:14 +0200 Subject: [PATCH 0225/2502] Base from_json on from_mapping --- flask/config.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/flask/config.py b/flask/config.py index 6119f02b..bb32f7a7 100644 --- a/flask/config.py +++ b/flask/config.py @@ -167,8 +167,8 @@ class Config(dict): def from_json(self, filename, silent=False): """Updates the values in the config from a JSON file. This function - behaves as if the JSON object was a dictionary and passed ot the - :meth:`from_object` function. + behaves as if the JSON object was a dictionary and passed to the + :meth:`from_mapping` function. :param filename: the filename of the JSON file. This can either be an absolute filename or a filename relative to the @@ -188,10 +188,7 @@ class Config(dict): return False e.strerror = 'Unable to load configuration file (%s)' % e.strerror raise - for key in obj.keys(): - if key.isupper(): - self[key] = obj[key] - return True + return self.from_mapping(obj) def from_mapping(self, *mapping, **kwargs): """Updates the config like :meth:`update` ignoring items with non-upper @@ -214,6 +211,7 @@ class Config(dict): for (key, value) in mapping: if key.isupper(): self[key] = value + return True def get_namespace(self, namespace, lowercase=True): """Returns a dictionary containing a subset of configuration options From 3c48bf893590fe87445a735584591201c3085e35 Mon Sep 17 00:00:00 2001 From: Jeffrey D Date: Tue, 29 Jul 2014 20:42:28 -0500 Subject: [PATCH 0226/2502] Addressed issue #1134 --- .../simple_page/templates/pages/layout.html | 2 +- examples/flaskr/templates/layout.html | 8 ++++---- examples/flaskr/templates/login.html | 10 +++++----- examples/flaskr/templates/show_entries.html | 10 +++++----- examples/jqueryexample/templates/index.html | 10 +++++----- examples/jqueryexample/templates/layout.html | 4 ++-- examples/minitwit/templates/layout.html | 12 ++++++------ examples/minitwit/templates/login.html | 10 +++++----- examples/minitwit/templates/register.html | 14 +++++++------- examples/minitwit/templates/timeline.html | 16 ++++++++-------- examples/persona/templates/layout.html | 8 ++++---- 11 files changed, 52 insertions(+), 52 deletions(-) diff --git a/examples/blueprintexample/simple_page/templates/pages/layout.html b/examples/blueprintexample/simple_page/templates/pages/layout.html index e74a5871..5eaa8fc6 100644 --- a/examples/blueprintexample/simple_page/templates/pages/layout.html +++ b/examples/blueprintexample/simple_page/templates/pages/layout.html @@ -1,6 +1,6 @@ Simple Page Blueprint -
+

This is blueprint example

A simple page blueprint is registered under / and /pages diff --git a/examples/flaskr/templates/layout.html b/examples/flaskr/templates/layout.html index cbdb9650..737b51b2 100644 --- a/examples/flaskr/templates/layout.html +++ b/examples/flaskr/templates/layout.html @@ -1,9 +1,9 @@ Flaskr - -

+ +

Flaskr

-
+
{% if not session.logged_in %} log in {% else %} @@ -11,7 +11,7 @@ {% endif %}
{% for message in get_flashed_messages() %} -
{{ message }}
+
{{ message }}
{% endfor %} {% block body %}{% endblock %}
diff --git a/examples/flaskr/templates/login.html b/examples/flaskr/templates/login.html index 6f70bb76..ed09aeba 100644 --- a/examples/flaskr/templates/login.html +++ b/examples/flaskr/templates/login.html @@ -1,14 +1,14 @@ {% extends "layout.html" %} {% block body %}

Login

- {% if error %}

Error: {{ error }}{% endif %} -

+ {% if error %}

Error: {{ error }}{% endif %} +

Username: -
+
Password: -
-
+
+
{% endblock %} diff --git a/examples/flaskr/templates/show_entries.html b/examples/flaskr/templates/show_entries.html index fabe65ec..9cbd3229 100644 --- a/examples/flaskr/templates/show_entries.html +++ b/examples/flaskr/templates/show_entries.html @@ -1,17 +1,17 @@ {% extends "layout.html" %} {% block body %} {% if session.logged_in %} -
+
Title: -
+
Text: -
-
+
+
{% endif %} -