From 671c67aac96ae2cdf9c554ba4e52c167862be6fe Mon Sep 17 00:00:00 2001 From: Mihir Singh Date: Wed, 16 Apr 2014 17:38:41 -0400 Subject: [PATCH 001/399] Append a 'Vary: Cookie' header to the response when the session has been accessed --- flask/sessions.py | 58 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 18 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 - - # 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 + if session.accessed: + + 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 002/399] 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 f05aeea6dd525d735b5e008aa176b1b839b08cca Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Sat, 25 Oct 2014 15:53:09 -0700 Subject: [PATCH 003/399] Correct the order of suggested syntax for extension imports According to https://github.com/mitsuhiko/flask/issues/1092#issuecomment-47118613 and https://github.com/mitsuhiko/flask/pull/1085#issuecomment-45466907 , the correct order to attempt to import extensions should be flask_foo, then flask.ext.foo, then flaskext_foo. --- docs/extensiondev.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 918187fe..e0c8f5f8 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -410,8 +410,8 @@ deprecated ``flaskext.foo``. Flask 0.8 introduces a redirect import system that lets uses import from ``flask.ext.foo`` and it will try ``flask_foo`` first and if that fails ``flaskext.foo``. -Flask extensions should urge users to import from ``flask.ext.foo`` -instead of ``flask_foo`` or ``flaskext_foo`` so that extensions can +Flask extensions should urge users to import from ``flask_foo`` +instead of ``flask.ext.foo`` or ``flaskext_foo`` so that extensions can transition to the new package name without affecting users. From 063a8da4f8b9237f425ade907ce97339eff52f56 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sun, 26 Oct 2014 01:09:56 +0200 Subject: [PATCH 004/399] Add changelog for #1218 --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 284c95d0..0e72642a 100644 --- a/CHANGES +++ b/CHANGES @@ -66,6 +66,8 @@ Version 1.0 - Don't leak exception info of already catched exceptions to context teardown handlers (pull request ``#1393``). - Allow custom Jinja environment subclasses (pull request ``#1422``). +- Updated extension dev guidelines. + Version 0.10.2 -------------- From 43d6b8a5fc5f4efd9dd1ce8c73a0c15979defd22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 02:01:12 +0200 Subject: [PATCH 005/399] Drop Extension==dev requirement pip doesn't install links included in the description of projects anymore. Therefore ==dev install doesn't work anymore. --- docs/extensiondev.rst | 70 ++++++++++++++++++++----------------------- 1 file changed, 33 insertions(+), 37 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index e0c8f5f8..395227ec 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -355,43 +355,39 @@ new releases. These approved extensions are listed on the `Flask Extension Registry`_ and marked appropriately. If you want your own extension to be approved you have to follow these guidelines: -0. An approved Flask extension requires a maintainer. In the event an - extension author would like to move beyond the project, the project should - find a new maintainer including full source hosting transition and PyPI - access. If no maintainer is available, give access to the Flask core team. -1. An approved Flask extension must provide exactly one package or module - named ``flask_extensionname``. They might also reside inside a - ``flaskext`` namespace packages though this is discouraged now. -2. It must ship a testing suite that can either be invoked with ``make test`` - or ``python setup.py test``. For test suites invoked with ``make - test`` the extension has to ensure that all dependencies for the test - are installed automatically. If tests are invoked with ``python setup.py - test``, test dependencies can be specified in the :file:`setup.py` file. The - test suite also has to be part of the distribution. -3. APIs of approved extensions will be checked for the following - characteristics: - - - an approved extension has to support multiple applications - running in the same Python process. - - it must be possible to use the factory pattern for creating - applications. - -4. The license must be BSD/MIT/WTFPL licensed. -5. The naming scheme for official extensions is *Flask-ExtensionName* or - *ExtensionName-Flask*. -6. Approved extensions must define all their dependencies in the - :file:`setup.py` file unless a dependency cannot be met because it is not - available on PyPI. -7. The extension must have documentation that uses one of the two Flask - themes for Sphinx documentation. -8. The setup.py description (and thus the PyPI description) has to - link to the documentation, website (if there is one) and there - must be a link to automatically install the development version - (``PackageName==dev``). -9. The ``zip_safe`` flag in the setup script must be set to ``False``, - even if the extension would be safe for zipping. -10. An extension currently has to support Python 2.6 as well as - Python 2.7 +0. An approved Flask extension requires a maintainer. In the event an + extension author would like to move beyond the project, the project should + find a new maintainer including full source hosting transition and PyPI + access. If no maintainer is available, give access to the Flask core team. +1. An approved Flask extension must provide exactly one package or module + named ``flask_extensionname``. They might also reside inside a + ``flaskext`` namespace packages though this is discouraged now. +2. It must ship a testing suite that can either be invoked with ``make test`` + or ``python setup.py test``. For test suites invoked with ``make + test`` the extension has to ensure that all dependencies for the test + are installed automatically. If tests are invoked with ``python setup.py + test``, test dependencies can be specified in the `setup.py` file. The + test suite also has to be part of the distribution. +3. APIs of approved extensions will be checked for the following + characteristics: + + - an approved extension has to support multiple applications + running in the same Python process. + - it must be possible to use the factory pattern for creating + applications. + +4. The license must be BSD/MIT/WTFPL licensed. +5. The naming scheme for official extensions is *Flask-ExtensionName* or + *ExtensionName-Flask*. +6. Approved extensions must define all their dependencies in the + `setup.py` file unless a dependency cannot be met because it is not + available on PyPI. +7. The extension must have documentation that uses one of the two Flask + themes for Sphinx documentation. +8. The ``zip_safe`` flag in the setup script must be set to ``False``, + even if the extension would be safe for zipping. +9. An extension currently has to support Python 2.6 as well as + Python 2.7 .. _ext-import-transition: From 3185f445c908a3926f17deb574cb9b1f0b049fe8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 02:04:21 +0200 Subject: [PATCH 006/399] Drop Python 2.6 minimum requirement for extensions Python 2.6 is not supported by python-dev anymore and does not get any security updates. Even though Flask supports 2.6 at the moment, I think it's not necessary for any extensions that are going to be approved in the future to support 2.6. --- docs/extensiondev.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 395227ec..8b18e726 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -386,8 +386,7 @@ extension to be approved you have to follow these guidelines: themes for Sphinx documentation. 8. The ``zip_safe`` flag in the setup script must be set to ``False``, even if the extension would be safe for zipping. -9. An extension currently has to support Python 2.6 as well as - Python 2.7 +9. An extension currently has to support Python 2.7. .. _ext-import-transition: From b12d9762e7fdb4877ea0cb750628149609e3ecb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 02:06:09 +0200 Subject: [PATCH 007/399] Require Python 3.3 and higher for extensions Flask and several extensions already supports Python 3.3 and higher. By requiring approved extensions to support Python 3.3 as well we can quickly achieve better Python 3 adoption and make using Python 3 easier for users. The effort of supporting both Python 2.7 and Python 3.3 is small enough that it shouldn't be a problem to require this from extension authors. --- docs/extensiondev.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 8b18e726..5b20528e 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -386,7 +386,7 @@ extension to be approved you have to follow these guidelines: themes for Sphinx documentation. 8. The ``zip_safe`` flag in the setup script must be set to ``False``, even if the extension would be safe for zipping. -9. An extension currently has to support Python 2.7. +9. An extension currently has to support Python 2.7, Python 3.3 and higher. .. _ext-import-transition: From b9938d0182abac79289963b2df507756bf86a50b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sun, 27 Apr 2014 00:26:39 +0200 Subject: [PATCH 008/399] Don't allow namespace packages anymore --- docs/extensiondev.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 5b20528e..000892e6 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -360,8 +360,7 @@ extension to be approved you have to follow these guidelines: find a new maintainer including full source hosting transition and PyPI access. If no maintainer is available, give access to the Flask core team. 1. An approved Flask extension must provide exactly one package or module - named ``flask_extensionname``. They might also reside inside a - ``flaskext`` namespace packages though this is discouraged now. + named ``flask_extensionname``. 2. It must ship a testing suite that can either be invoked with ``make test`` or ``python setup.py test``. For test suites invoked with ``make test`` the extension has to ensure that all dependencies for the test From 2b58e6120ceadf363d12cc12889bfc79c18d5066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sun, 27 Apr 2014 00:27:37 +0200 Subject: [PATCH 009/399] Fix wording --- docs/extensiondev.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 000892e6..e8028ffc 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -375,7 +375,7 @@ extension to be approved you have to follow these guidelines: - it must be possible to use the factory pattern for creating applications. -4. The license must be BSD/MIT/WTFPL licensed. +4. The extension must be BSD/MIT/WTFPL licensed. 5. The naming scheme for official extensions is *Flask-ExtensionName* or *ExtensionName-Flask*. 6. Approved extensions must define all their dependencies in the From f80ea4fe5d3f194c567d3fd279e1e84050a12b10 Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Sat, 25 Oct 2014 18:56:10 -0700 Subject: [PATCH 010/399] Some grammar and typo fixes --- docs/extensiondev.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index e8028ffc..2f8aae39 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -48,7 +48,7 @@ that people can easily install the development version into their virtualenv without having to download the library by hand. Flask extensions must be licensed under a BSD, MIT or more liberal license -to be able to be enlisted in the Flask Extension Registry. Keep in mind +in order to be listed in the Flask Extension Registry. Keep in mind that the Flask Extension Registry is a moderated place and libraries will be reviewed upfront if they behave as required. @@ -154,10 +154,10 @@ What to use depends on what you have in mind. For the SQLite 3 extension we will use the class-based approach because it will provide users with an object that handles opening and closing database connections. -What's important about classes is that they encourage to be shared around -on module level. In that case, the object itself must not under any +When designing your classes, it's important to make them easily reusable +at the module level. This means the object itself must not under any circumstances store any application specific state and must be shareable -between different application. +between different applications. The Extension Code ------------------ @@ -334,10 +334,10 @@ development. If you want to learn more, it's a very good idea to check out existing extensions on the `Flask Extension Registry`_. If you feel lost there is still the `mailinglist`_ and the `IRC channel`_ to get some ideas for nice looking APIs. Especially if you do something nobody before -you did, it might be a very good idea to get some more input. This not -only to get an idea about what people might want to have from an -extension, but also to avoid having multiple developers working on pretty -much the same side by side. +you did, it might be a very good idea to get some more input. This not only +generates useful feedback on what people might want from an extension, but +also avoids having multiple developers working in isolation on pretty much the +same problem. Remember: good API design is hard, so introduce your project on the mailinglist, and let other developers give you a helping hand with From 011b129b6bc615c7a24de626dd63b6a311fa6ce6 Mon Sep 17 00:00:00 2001 From: Jimmy McCarthy Date: Mon, 8 Jun 2015 16:31:35 -0500 Subject: [PATCH 011/399] Add kwarg to disable auto OPTIONS on add_url_rule Adds support for a kwarg `provide_automatic_options` on `add_url_rule`, which lets you turn off the automatic OPTIONS response on a per-URL basis even if your view functions are functions, not classes (so you can't provide attrs on them). --- flask/app.py | 6 ++++-- tests/test_basic.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/flask/app.py b/flask/app.py index f0a8b69b..5b186577 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1013,8 +1013,10 @@ class Flask(_PackageBoundObject): # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. - provide_automatic_options = getattr(view_func, - 'provide_automatic_options', None) + provide_automatic_options = options.pop( + 'provide_automatic_options', getattr(view_func, + 'provide_automatic_options', None)) + if provide_automatic_options is None: if 'OPTIONS' not in methods: diff --git a/tests/test_basic.py b/tests/test_basic.py index 695e346e..08e03aca 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1604,3 +1604,44 @@ def test_run_server_port(monkeypatch): hostname, port = 'localhost', 8000 app.run(hostname, port, debug=True) assert rv['result'] == 'running on %s:%s ...' % (hostname, port) + + +def test_disable_automatic_options(): + # Issue 1488: Add support for a kwarg to add_url_rule to disable the auto OPTIONS response + app = flask.Flask(__name__) + + def index(): + return flask.request.method + + def more(): + return flask.request.method + + app.add_url_rule('/', 'index', index, provide_automatic_options=False) + app.add_url_rule('/more', 'more', more, methods=['GET', 'POST'], provide_automatic_options=False) + + c = app.test_client() + assert c.get('/').data == b'GET' + rv = c.post('/') + assert rv.status_code == 405 + assert sorted(rv.allow) == ['GET', 'HEAD'] + # Older versions of Werkzeug.test.Client don't have an options method + if hasattr(c, 'options'): + rv = c.options('/') + else: + rv = c.open('/', method='OPTIONS') + assert rv.status_code == 405 + + rv = c.head('/') + assert rv.status_code == 200 + assert not rv.data # head truncates + assert c.post('/more').data == b'POST' + assert c.get('/more').data == b'GET' + rv = c.delete('/more') + assert rv.status_code == 405 + assert sorted(rv.allow) == ['GET', 'HEAD', 'POST'] + # Older versions of Werkzeug.test.Client don't have an options method + if hasattr(c, 'options'): + rv = c.options('/more') + else: + rv = c.open('/more', method='OPTIONS') + assert rv.status_code == 405 From a1360196447c8dad1b20fc611bd96210c0bfca28 Mon Sep 17 00:00:00 2001 From: Jimmy McCarthy Date: Tue, 7 Jul 2015 13:21:51 -0500 Subject: [PATCH 012/399] Updated changelog --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index b33c3795..d1529526 100644 --- a/CHANGES +++ b/CHANGES @@ -70,6 +70,9 @@ Version 1.0 - ``flask.g`` now has ``pop()`` and ``setdefault`` methods. - Turn on autoescape for ``flask.templating.render_template_string`` by default (pull request ``#1515``). +- Added support for `provide_automatic_options` in `**kwargs` on + :meth:`add_url_rule` to turn off automatic OPTIONS when the `view_func` + argument is not a class (pull request ``#1489``). Version 0.10.2 -------------- From 5505827a4bd759a24696258983ec14c768358af8 Mon Sep 17 00:00:00 2001 From: Jimmy McCarthy Date: Mon, 14 Sep 2015 13:25:52 -0500 Subject: [PATCH 013/399] provide_automatic_options as explicit arg In add_url_rule, break provide_automatic_options out to an explicit kwarg, and add notes to the docstring. --- flask/app.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/flask/app.py b/flask/app.py index e03ff221..de147e9e 100644 --- a/flask/app.py +++ b/flask/app.py @@ -944,7 +944,8 @@ class Flask(_PackageBoundObject): return iter(self._blueprint_order) @setupmethod - def add_url_rule(self, rule, endpoint=None, view_func=None, **options): + def add_url_rule(self, rule, endpoint=None, view_func=None, + provide_automatic_options=None, **options): """Connects a URL rule. Works exactly like the :meth:`route` decorator. If a view_func is provided it will be registered with the endpoint. @@ -984,6 +985,10 @@ class Flask(_PackageBoundObject): endpoint :param view_func: the function to call when serving a request to the provided endpoint + :param provide_automatic_options: controls whether ``OPTIONS`` should + be provided automatically. If this + is not set, will check attributes on + the view or list of methods. :param options: the options to be forwarded to the underlying :class:`~werkzeug.routing.Rule` object. A change to Werkzeug is handling of method options. methods @@ -1013,10 +1018,9 @@ class Flask(_PackageBoundObject): # starting with Flask 0.8 the view_func object can disable and # force-enable the automatic options handling. - provide_automatic_options = options.pop( - 'provide_automatic_options', getattr(view_func, - 'provide_automatic_options', None)) - + if provide_automatic_options is None: + provide_automatic_options = getattr(view_func, + 'provide_automatic_options', None) if provide_automatic_options is None: if 'OPTIONS' not in methods: From a23ce4697155ae236c139c96c6d9dd65207a0cb6 Mon Sep 17 00:00:00 2001 From: Jimmy McCarthy Date: Mon, 14 Sep 2015 13:29:16 -0500 Subject: [PATCH 014/399] Update change log --- CHANGES | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index cac78f73..b459fc1d 100644 --- a/CHANGES +++ b/CHANGES @@ -70,9 +70,9 @@ Version 1.0 - ``flask.g`` now has ``pop()`` and ``setdefault`` methods. - Turn on autoescape for ``flask.templating.render_template_string`` by default (pull request ``#1515``). -- Added support for `provide_automatic_options` in `**kwargs` on - :meth:`add_url_rule` to turn off automatic OPTIONS when the `view_func` - argument is not a class (pull request ``#1489``). +- Added support for `provide_automatic_options` in :meth:`add_url_rule` to + turn off automatic OPTIONS when the `view_func` argument is not a class + (pull request ``#1489``). Version 0.10.2 -------------- From 51c35295af94f8f401a4942aab674f161222a984 Mon Sep 17 00:00:00 2001 From: Nadav Geva Date: Thu, 15 Oct 2015 00:02:46 +0300 Subject: [PATCH 015/399] Fix name mismatch in apierrors page --- 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 daa3f272da477470d23481d409d54f989136ac32 Mon Sep 17 00:00:00 2001 From: Ivan Velichko Date: Fri, 20 Nov 2015 19:43:49 +0300 Subject: [PATCH 016/399] Allow to specify subdomain and/or url_scheme in app.test_request_context() --- flask/app.py | 6 +++++- flask/testing.py | 19 +++++++++++++++---- flask/testsuite/testing.py | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 5 deletions(-) diff --git a/flask/app.py b/flask/app.py index e073798a..8a191d0a 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1776,7 +1776,11 @@ 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 - function accepts the same arguments). + function accepts the same arguments plus two additional). + + Additional arguments (might be used only if `base_url` is not specified): + :param subdomain: subdomain in case of testing requests handled by blueprint + :param url_scheme: a URL scheme (default scheme is http) """ from flask.testing import make_test_environ_builder builder = make_test_environ_builder(self, *args, **kwargs) diff --git a/flask/testing.py b/flask/testing.py index 6351462b..73043f1b 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -20,13 +20,24 @@ except ImportError: from urlparse import urlsplit as url_parse -def make_test_environ_builder(app, path='/', base_url=None, *args, **kwargs): +def make_test_environ_builder(app, path='/', base_url=None, subdomain=None, + url_scheme=None, *args, **kwargs): """Creates a new test builder with some application defaults thrown in.""" - http_host = app.config.get('SERVER_NAME') - app_root = app.config.get('APPLICATION_ROOT') + assert not (base_url or subdomain or url_scheme) \ + or (base_url is not None) != bool(subdomain or url_scheme), \ + 'If "base_url" parameter is passed in, pass of ' \ + '"subdomain" and/or "url_scheme" is meaningless.' + if base_url is None: + http_host = app.config.get('SERVER_NAME') + app_root = app.config.get('APPLICATION_ROOT') + if subdomain: + http_host = '%s.%s' % (subdomain, http_host) + if url_scheme is None: + url_scheme = 'http' + url = url_parse(path) - base_url = 'http://%s/' % (url.netloc or http_host or 'localhost') + base_url = '%s://%s/' % (url_scheme, url.netloc or http_host or 'localhost') if app_root: base_url += app_root.lstrip('/') if url.netloc: diff --git a/flask/testsuite/testing.py b/flask/testsuite/testing.py index 11ab763b..9d58fef4 100644 --- a/flask/testsuite/testing.py +++ b/flask/testsuite/testing.py @@ -45,6 +45,38 @@ class TestToolsTestCase(FlaskTestCase): rv = c.get('/') self.assert_equal(rv.data, b'http://localhost/') + def test_specify_url_scheme(self): + app = flask.Flask(__name__) + app.testing = True + @app.route('/') + def index(): + return flask.request.url + + ctx = app.test_request_context(url_scheme='https') + self.assert_equal(ctx.request.url, 'https://localhost/') + with app.test_client() as c: + rv = c.get('/', url_scheme='https') + self.assert_equal(rv.data, b'https://localhost/') + + def test_blueprint_with_subdomain(self): + app = flask.Flask(__name__) + app.testing = True + app.config['SERVER_NAME'] = 'example.com:1234' + app.config['APPLICATION_ROOT'] = '/foo' + + bp = flask.Blueprint('company', __name__, subdomain='xxx') + @bp.route('/') + def index(): + return flask.request.url + app.register_blueprint(bp) + + ctx = app.test_request_context('/', subdomain='xxx') + self.assert_equal(ctx.request.url, 'http://xxx.example.com:1234/foo/') + self.assert_equal(ctx.request.blueprint, bp.name) + with app.test_client() as c: + rv = c.get('/', subdomain='xxx') + self.assert_equal(rv.data, b'http://xxx.example.com:1234/foo/') + def test_redirect_keep_session(self): app = flask.Flask(__name__) app.secret_key = 'testing' From 826d7475cdab43b85d79f99e8eae5dabb01baf7b Mon Sep 17 00:00:00 2001 From: Aviv Cohn Date: Tue, 5 Jan 2016 01:10:35 +0200 Subject: [PATCH 017/399] Clarified the docstring in method Flask.preprocess_request. The doc now clearly states that the method invokes two set of hook functions, and how these are filtered before execution. --- flask/app.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/flask/app.py b/flask/app.py index 91139773..33b543dd 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1791,16 +1791,18 @@ class Flask(_PackageBoundObject): raise error def preprocess_request(self): - """Called before the actual request dispatching and will - call each :meth:`before_request` decorated function, passing no - arguments. - If any of these functions returns a value, it's handled as - if it was the return value from the view and further - request handling is stopped. + """Called before the request dispatching. - This also triggers the :meth:`url_value_processor` functions before - the actual :meth:`before_request` functions are called. + Triggers two set of hook functions that should be invoked prior to request dispatching: + :attr:`url_value_preprocessors` and :attr:`before_request_funcs` + (the latter are functions decorated with :meth:`before_request` decorator). + In both cases, the method triggers only the functions that are either global + or registered to the current blueprint. + + If any function in :attr:`before_request_funcs` returns a value, it's handled as if it was + the return value from the view function, and further request handling is stopped. """ + bp = _request_ctx_stack.top.request.blueprint funcs = self.url_value_preprocessors.get(None, ()) From 954b7ef7bbc261ac455feb122b56e897653de826 Mon Sep 17 00:00:00 2001 From: Randy Liou Date: Fri, 3 Jun 2016 15:50:38 -0700 Subject: [PATCH 018/399] Enhance code coverage for Blueprint.endpoint Add basic test for the endpoint decorator for the Blueprint object. --- tests/test_blueprints.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index a3309037..de293e7f 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -355,6 +355,25 @@ def test_route_decorator_custom_endpoint_with_dots(): rv = c.get('/py/bar/123') assert rv.status_code == 404 + +def test_endpoint_decorator(): + from werkzeug.routing import Rule + app = flask.Flask(__name__) + app.url_map.add(Rule('/foo', endpoint='bar')) + + bp = flask.Blueprint('bp', __name__) + + @bp.endpoint('bar') + def foobar(): + return flask.request.endpoint + + app.register_blueprint(bp, url_prefix='/bp_prefix') + + c = app.test_client() + assert c.get('/foo').data == b'bar' + assert c.get('/bp_prefix/bar').status_code == 404 + + def test_template_filter(): bp = flask.Blueprint('bp', __name__) @bp.app_template_filter() From 19dcdf4e70e3c57ef647c16c63f6633241c0c946 Mon Sep 17 00:00:00 2001 From: Zev Averbach Date: Fri, 3 Jun 2016 16:59:15 -0700 Subject: [PATCH 019/399] enumerates the states in which code is executed... ... and fixes an awkward phrasing. --- docs/appcontext.rst | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/appcontext.rst b/docs/appcontext.rst index baa71315..44a21785 100644 --- a/docs/appcontext.rst +++ b/docs/appcontext.rst @@ -5,12 +5,15 @@ The Application Context .. versionadded:: 0.9 -One of the design ideas behind Flask is that there are two different -“states” in which code is executed. The application setup state in which -the application implicitly is on the module level. It starts when the -:class:`Flask` object is instantiated, and it implicitly ends when the -first request comes in. While the application is in this state a few -assumptions are true: +One of the design ideas behind Flask is that there are at least two +different “states” in which code is executed: + +1. The application setup state, in which the application implicitly is +on the module level. + +This state starts when the :class:`Flask` object is instantiated, and +it implicitly ends when the first request comes in. While the +application is in this state, a few assumptions are true: - the programmer can modify the application object safely. - no request handling happened so far @@ -18,18 +21,21 @@ assumptions are true: modify it, there is no magic proxy that can give you a reference to the application object you're currently creating or modifying. -In contrast, during request handling, a couple of other rules exist: +2. In contrast, in the request handling state, a couple of other rules +exist: - while a request is active, the context local objects (:data:`flask.request` and others) point to the current request. - any code can get hold of these objects at any time. -There is a third state which is sitting in between a little bit. +3. There is also a third state somewhere in between 'module-level' and +'request-handling': + Sometimes you are dealing with an application in a way that is similar to -how you interact with applications during request handling; just that there -is no request active. Consider, for instance, that you're sitting in an -interactive Python shell and interacting with the application, or a -command line application. +how you interact with applications during request handling, but without +there being an active request. Consider, for instance, that you're +sitting in an interactive Python shell and interacting with the +application, or a command line application. The application context is what powers the :data:`~flask.current_app` context local. From 501b8590dd8b262c93d52c92ab4b743af1f5d317 Mon Sep 17 00:00:00 2001 From: RamiC Date: Wed, 8 Jun 2016 12:03:26 +0300 Subject: [PATCH 020/399] Allow per blueprint json encoder decoder re #1710 --- flask/blueprints.py | 7 +++++++ flask/json.py | 6 ++++-- tests/test_helpers.py | 39 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/flask/blueprints.py b/flask/blueprints.py index 586a1b0b..62675204 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -89,6 +89,13 @@ class Blueprint(_PackageBoundObject): warn_on_modifications = False _got_registered_once = False + #: Blueprint local JSON decoder class to use. + # Set to None to use the :class:`~flask.app.Flask.json_encoder`. + json_encoder = None + #: Blueprint local JSON decoder class to use. + # Set to None to use the :class:`~flask.app.Flask.json_decoder`. + json_decoder = None + def __init__(self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, diff --git a/flask/json.py b/flask/json.py index b9ce4a08..ad0e7bdf 100644 --- a/flask/json.py +++ b/flask/json.py @@ -94,7 +94,8 @@ class JSONDecoder(_json.JSONDecoder): def _dump_arg_defaults(kwargs): """Inject default arguments for dump functions.""" if current_app: - kwargs.setdefault('cls', current_app.json_encoder) + bp = current_app.blueprints.get(request.blueprint, None) + kwargs.setdefault('cls', bp.json_encoder if bp else current_app.json_encoder) if not current_app.config['JSON_AS_ASCII']: kwargs.setdefault('ensure_ascii', False) kwargs.setdefault('sort_keys', current_app.config['JSON_SORT_KEYS']) @@ -106,7 +107,8 @@ def _dump_arg_defaults(kwargs): def _load_arg_defaults(kwargs): """Inject default arguments for load functions.""" if current_app: - kwargs.setdefault('cls', current_app.json_decoder) + bp = current_app.blueprints.get(request.blueprint, None) + kwargs.setdefault('cls', bp.json_decoder if bp else current_app.json_decoder) else: kwargs.setdefault('cls', JSONDecoder) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 3ff5900b..e893004a 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -90,12 +90,12 @@ class TestJSON(object): app = flask.Flask(__name__) app.config['JSON_AS_ASCII'] = True - with app.app_context(): + with app.test_request_context(): rv = flask.json.dumps(u'\N{SNOWMAN}') assert rv == '"\\u2603"' app.config['JSON_AS_ASCII'] = False - with app.app_context(): + with app.test_request_context(): rv = flask.json.dumps(u'\N{SNOWMAN}') assert rv == u'"\u2603"' @@ -234,6 +234,41 @@ class TestJSON(object): }), content_type='application/json') assert rv.data == b'"<42>"' + def test_blueprint_json_customization(self): + class X(object): + def __init__(self, val): + self.val = val + class MyEncoder(flask.json.JSONEncoder): + def default(self, o): + if isinstance(o, X): + return '<%d>' % o.val + return flask.json.JSONEncoder.default(self, o) + class MyDecoder(flask.json.JSONDecoder): + def __init__(self, *args, **kwargs): + kwargs.setdefault('object_hook', self.object_hook) + flask.json.JSONDecoder.__init__(self, *args, **kwargs) + def object_hook(self, obj): + if len(obj) == 1 and '_foo' in obj: + return X(obj['_foo']) + return obj + + blue = flask.Blueprint('blue', __name__) + blue.json_encoder = MyEncoder + blue.json_decoder = MyDecoder + @blue.route('/bp', methods=['POST']) + def index(): + return flask.json.dumps(flask.request.get_json()['x']) + + app = flask.Flask(__name__) + app.testing = True + app.register_blueprint(blue) + + c = app.test_client() + rv = c.post('/bp', data=flask.json.dumps({ + 'x': {'_foo': 42} + }), content_type='application/json') + assert rv.data == b'"<42>"' + def test_modified_url_encoding(self): class ModifiedRequest(flask.Request): url_charset = 'euc-kr' From 4305ebdf6692956a7e21a7195b90505d92f559d8 Mon Sep 17 00:00:00 2001 From: RamiC Date: Wed, 8 Jun 2016 12:50:43 +0300 Subject: [PATCH 021/399] Check for a request ctx before using the request. Use the app json coder when blueprint json coder is set to none. Revert the failling test to using an app_context re #1710 --- flask/json.py | 15 +++++++++++---- tests/test_helpers.py | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/flask/json.py b/flask/json.py index ad0e7bdf..fd39e539 100644 --- a/flask/json.py +++ b/flask/json.py @@ -13,6 +13,7 @@ import uuid from datetime import date from .globals import current_app, request from ._compat import text_type, PY2 +from .ctx import has_request_context from werkzeug.http import http_date from jinja2 import Markup @@ -94,8 +95,11 @@ class JSONDecoder(_json.JSONDecoder): def _dump_arg_defaults(kwargs): """Inject default arguments for dump functions.""" if current_app: - bp = current_app.blueprints.get(request.blueprint, None) - kwargs.setdefault('cls', bp.json_encoder if bp else current_app.json_encoder) + bp = current_app.blueprints.get(request.blueprint, + None) if has_request_context() else None + kwargs.setdefault('cls', + bp.json_encoder if bp and bp.json_encoder + else current_app.json_encoder) if not current_app.config['JSON_AS_ASCII']: kwargs.setdefault('ensure_ascii', False) kwargs.setdefault('sort_keys', current_app.config['JSON_SORT_KEYS']) @@ -107,8 +111,11 @@ def _dump_arg_defaults(kwargs): def _load_arg_defaults(kwargs): """Inject default arguments for load functions.""" if current_app: - bp = current_app.blueprints.get(request.blueprint, None) - kwargs.setdefault('cls', bp.json_decoder if bp else current_app.json_decoder) + bp = current_app.blueprints.get(request.blueprint, + None) if has_request_context() else None + kwargs.setdefault('cls', + bp.json_decoder if bp and bp.json_decoder + else current_app.json_decoder) else: kwargs.setdefault('cls', JSONDecoder) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index e893004a..142f555f 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -90,12 +90,12 @@ class TestJSON(object): app = flask.Flask(__name__) app.config['JSON_AS_ASCII'] = True - with app.test_request_context(): + with app.app_context(): rv = flask.json.dumps(u'\N{SNOWMAN}') assert rv == '"\\u2603"' app.config['JSON_AS_ASCII'] = False - with app.test_request_context(): + with app.app_context(): rv = flask.json.dumps(u'\N{SNOWMAN}') assert rv == u'"\u2603"' From 5cadd4a348b4d42e22ddb2bfad93f74a4f34cc59 Mon Sep 17 00:00:00 2001 From: Anton Sarukhanov Date: Wed, 8 Jun 2016 08:26:01 -0400 Subject: [PATCH 022/399] Added make target for test coverage, documented make commands --- .gitignore | 2 ++ CONTRIBUTING.rst | 25 ++++++++++++++++--------- Makefile | 5 +++++ test-requirements.txt | 2 ++ 4 files changed, 25 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 9bf4f063..e754af19 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,5 @@ _mailinglist .tox .cache/ .idea/ +.coverage +htmlcov diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index ca7b4af2..f4d1ef9c 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -31,18 +31,12 @@ Submitting patches - Try to follow `PEP8 `_, but you may ignore the line-length-limit if following it would make the code uglier. - -Running the testsuite ---------------------- +Getting Started +=============== You probably want to set up a `virtualenv `_. -The minimal requirement for running the testsuite is ``py.test``. You can -install it with:: - - pip install pytest - Clone this repository:: git clone https://github.com/pallets/flask.git @@ -52,11 +46,21 @@ Install Flask as an editable package using the current source:: cd flask pip install --editable . +Running the testsuite +--------------------- + +The minimal requirement for running the testsuite is ``pytest``. You can +install it with:: + + pip install pytest + Then you can run the testsuite with:: py.test -With only py.test installed, a large part of the testsuite will get skipped +**Shortcut**: ``make test`` will ensure ``pytest`` is installed, and run it. + +With only pytest installed, a large part of the testsuite will get skipped though. Whether this is relevant depends on which part of Flask you're working on. Travis is set up to run the full testsuite when you submit your pull request anyways. @@ -69,6 +73,8 @@ of ``pytest``. You can install it with:: The ``tox`` command will then run all tests against multiple combinations Python versions and dependency versions. +**Shortcut**: ``make tox-test`` will ensure ``tox`` is installed, and run it. + Running test coverage --------------------- Generating a report of lines that do not have unit test coverage can indicate where @@ -87,3 +93,4 @@ Generate a HTML report can be done using this command:: Full docs on ``coverage.py`` are here: https://coverage.readthedocs.io +**Shortcut**: ``make cov`` will ensure ``pytest-cov`` is installed, run it, display the results, *and* save the HTML report. diff --git a/Makefile b/Makefile index 9bcdebc2..3fcb765d 100644 --- a/Makefile +++ b/Makefile @@ -7,8 +7,13 @@ test: FLASK_DEBUG= py.test tests examples tox-test: + pip install -r test-requirements.txt -q tox +cov: + pip install -r test-requirements.txt -q + FLASK_DEBUG= py.test --cov-report term --cov-report html --cov=flask --cov=examples tests examples + audit: python setup.py audit diff --git a/test-requirements.txt b/test-requirements.txt index e079f8a6..8be82dd0 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1,3 @@ +tox pytest +pytest-cov From 0514ba2de1751de1b0c9a223c96f6a7145714e6a Mon Sep 17 00:00:00 2001 From: Dave Barker Date: Tue, 14 Jun 2016 00:59:32 +0100 Subject: [PATCH 023/399] Enable template auto-reloading in app.run() When Flask app debugging is enabled (app.debug==True), and Jinja2 template auto-reloading is not explicitly disbaled, template auto-reloading should be enabled. If the app is instantiated, the jinja_env object is accessed (thereby initialising the Jinja2 environment) and the server is then started with app.run(debug=True), template auto-reloading is *not* enabled. This is because reading the jinja_env object causes the environment initialisation function to set auto_reload to app.debug (which isn't yet True). Calling app.run(debug=True) should correct this in order to remain consistent with Flask code reloading (which is enabled within app.run() if debug == True). --- flask/app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flask/app.py b/flask/app.py index b1ea0464..1d32ece9 100644 --- a/flask/app.py +++ b/flask/app.py @@ -839,6 +839,8 @@ class Flask(_PackageBoundObject): options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) options.setdefault('passthrough_errors', True) + if debug: + self.jinja_env.auto_reload = True try: run_simple(host, port, self, **options) finally: From 24289e97af233f11d9c145f80604b2ee416254a1 Mon Sep 17 00:00:00 2001 From: Dave Barker Date: Wed, 15 Jun 2016 02:15:33 +0100 Subject: [PATCH 024/399] Add test for new template auto reload debug behaviour --- tests/test_templating.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_templating.py b/tests/test_templating.py index b60a592a..e65fe834 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -14,6 +14,7 @@ import pytest import flask import logging from jinja2 import TemplateNotFound +import werkzeug.serving def test_context_processing(): @@ -346,6 +347,22 @@ def test_templates_auto_reload(): app.config['TEMPLATES_AUTO_RELOAD'] = True assert app.jinja_env.auto_reload is True +def test_templates_auto_reload_debug_run(monkeypatch): + # debug is None in config, config option is None, app.run(debug=True) + rv = {} + + # Mocks werkzeug.serving.run_simple method + def run_simple_mock(*args, **kwargs): + rv['passthrough_errors'] = kwargs.get('passthrough_errors') + + app = flask.Flask(__name__) + monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) + + assert app.config['TEMPLATES_AUTO_RELOAD'] is None + assert app.jinja_env.auto_reload is False + app.run(debug=True) + assert app.jinja_env.auto_reload is True + def test_template_loader_debugging(test_apps): from blueprintapp import app From 1a67e284d043818743ae9ef5a46b6b64a9db56f7 Mon Sep 17 00:00:00 2001 From: Dave Barker Date: Wed, 15 Jun 2016 02:25:48 +0100 Subject: [PATCH 025/399] Remove unnecessary werkzeug mock attribs from test --- tests/test_templating.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_templating.py b/tests/test_templating.py index e65fe834..aa3d7409 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -349,11 +349,9 @@ def test_templates_auto_reload(): def test_templates_auto_reload_debug_run(monkeypatch): # debug is None in config, config option is None, app.run(debug=True) - rv = {} - # Mocks werkzeug.serving.run_simple method def run_simple_mock(*args, **kwargs): - rv['passthrough_errors'] = kwargs.get('passthrough_errors') + pass app = flask.Flask(__name__) monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) From 16471795117bb2d200888e49af5b621377bc1436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiss=20Gy=C3=B6rgy?= Date: Sun, 26 Apr 2015 09:40:20 +0200 Subject: [PATCH 026/399] Added routes command, which shows all the endpoints registered for the app. Orderable by rules, endpoints and methods. Shows up in the builtin command list. --- flask/cli.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/flask/cli.py b/flask/cli.py index 3796c083..2565657f 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -304,6 +304,7 @@ class FlaskGroup(AppGroup): if add_default_commands: self.add_command(run_command) self.add_command(shell_command) + self.add_command(routes_command) self._loaded_plugin_commands = False @@ -461,6 +462,33 @@ def shell_command(): code.interact(banner=banner, local=ctx) +@click.command('routes', short_help='Show routes for the app.') +@click.option('-r', 'order_by', flag_value='rule', default=True, help='Order by route') +@click.option('-e', 'order_by', flag_value='endpoint', help='Order by endpoint') +@click.option('-m', 'order_by', flag_value='methods', help='Order by methods') +@with_appcontext +def routes_command(order_by): + """Show all routes with endpoints and methods.""" + from flask.globals import _app_ctx_stack + app = _app_ctx_stack.top.app + + order_key = lambda rule: getattr(rule, order_by) + sorted_rules = sorted(app.url_map.iter_rules(), key=order_key) + + max_rule = max(len(rule.rule) for rule in sorted_rules) + max_ep = max(len(rule.endpoint) for rule in sorted_rules) + max_meth = max(len(', '.join(rule.methods)) for rule in sorted_rules) + + columnformat = '{:<%s} {:<%s} {:<%s}' % (max_rule, max_ep, max_meth) + click.echo(columnformat.format('Route', 'Endpoint', 'Methods')) + under_count = max_rule + max_ep + max_meth + 4 + click.echo('-' * under_count) + + for rule in sorted_rules: + methods = ', '.join(rule.methods) + click.echo(columnformat.format(rule.rule, rule.endpoint, methods)) + + cli = FlaskGroup(help="""\ This shell command acts as general utility script for Flask applications. From b8e826c16bbbada9128b70357c218ef79425499d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiss=20Gy=C3=B6rgy?= Date: Sat, 25 Jun 2016 13:17:33 +0200 Subject: [PATCH 027/399] Added tests, fixed some minor alignment problems. --- flask/cli.py | 3 +++ tests/test_apps/cliapp/routesapp.py | 18 +++++++++++++ tests/test_cli.py | 39 ++++++++++++++++++++++++++++- 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 tests/test_apps/cliapp/routesapp.py diff --git a/flask/cli.py b/flask/cli.py index 2565657f..e4992598 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -476,8 +476,11 @@ def routes_command(order_by): sorted_rules = sorted(app.url_map.iter_rules(), key=order_key) max_rule = max(len(rule.rule) for rule in sorted_rules) + max_rule = max(max_rule, len('Route')) max_ep = max(len(rule.endpoint) for rule in sorted_rules) + max_ep = max(max_ep, len('Endpoint')) max_meth = max(len(', '.join(rule.methods)) for rule in sorted_rules) + max_meth = max(max_meth, len('Methods')) columnformat = '{:<%s} {:<%s} {:<%s}' % (max_rule, max_ep, max_meth) click.echo(columnformat.format('Route', 'Endpoint', 'Methods')) diff --git a/tests/test_apps/cliapp/routesapp.py b/tests/test_apps/cliapp/routesapp.py new file mode 100644 index 00000000..84060546 --- /dev/null +++ b/tests/test_apps/cliapp/routesapp.py @@ -0,0 +1,18 @@ +from __future__ import absolute_import, print_function + +from flask import Flask + + +noroute_app = Flask('noroute app') +simpleroute_app = Flask('simpleroute app') +only_POST_route_app = Flask('GET route app') + + +@simpleroute_app.route('/simpleroute') +def simple(): + pass + + +@only_POST_route_app.route('/only-post', methods=['POST']) +def only_post(): + pass diff --git a/tests/test_cli.py b/tests/test_cli.py index 4a3d0831..7df00167 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -20,7 +20,7 @@ import pytest from click.testing import CliRunner from flask import Flask, current_app -from flask.cli import AppGroup, FlaskGroup, NoAppException, ScriptInfo, \ +from flask.cli import cli, AppGroup, FlaskGroup, NoAppException, ScriptInfo, \ find_best_app, locate_app, with_appcontext, prepare_exec_for_file, \ find_default_import_path @@ -170,3 +170,40 @@ def test_flaskgroup(): result = runner.invoke(cli, ['test']) assert result.exit_code == 0 assert result.output == 'flaskgroup\n' + + +class TestRoutes: + def test_no_route(self, monkeypatch): + monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:noroute_app') + runner = CliRunner() + result = runner.invoke(cli, ['routes'], catch_exceptions=False) + assert result.exit_code == 0 + assert result.output == """\ +Route Endpoint Methods +----------------------------------------------------- +/static/ static HEAD, OPTIONS, GET +""" + + def test_simple_route(self, monkeypatch): + monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:simpleroute_app') + runner = CliRunner() + result = runner.invoke(cli, ['routes'], catch_exceptions=False) + assert result.exit_code == 0 + assert result.output == """\ +Route Endpoint Methods +----------------------------------------------------- +/simpleroute simple HEAD, OPTIONS, GET +/static/ static HEAD, OPTIONS, GET +""" + + def test_only_POST_route(self, monkeypatch): + monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:only_POST_route_app') + runner = CliRunner() + result = runner.invoke(cli, ['routes'], catch_exceptions=False) + assert result.exit_code == 0 + assert result.output == """\ +Route Endpoint Methods +------------------------------------------------------ +/only-post only_post POST, OPTIONS +/static/ static HEAD, OPTIONS, GET +""" From 1b764cff93696a75c6248b6f956426cbefa097bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kiss=20Gy=C3=B6rgy?= Date: Sat, 25 Jun 2016 13:24:43 +0200 Subject: [PATCH 028/399] Added runner fixture --- tests/test_cli.py | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 7df00167..db82ae8e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -25,6 +25,11 @@ from flask.cli import cli, AppGroup, FlaskGroup, NoAppException, ScriptInfo, \ find_default_import_path +@pytest.fixture +def runner(): + return CliRunner() + + def test_cli_name(test_apps): """Make sure the CLI object's name is the app's name and not the app itself""" from cliapp.app import testapp @@ -108,7 +113,7 @@ def test_scriptinfo(test_apps): assert obj.load_app() == app -def test_with_appcontext(): +def test_with_appcontext(runner): """Test of with_appcontext.""" @click.command() @with_appcontext @@ -117,13 +122,12 @@ def test_with_appcontext(): obj = ScriptInfo(create_app=lambda info: Flask("testapp")) - runner = CliRunner() result = runner.invoke(testcmd, obj=obj) assert result.exit_code == 0 assert result.output == 'testapp\n' -def test_appgroup(): +def test_appgroup(runner): """Test of with_appcontext.""" @click.group(cls=AppGroup) def cli(): @@ -143,7 +147,6 @@ def test_appgroup(): obj = ScriptInfo(create_app=lambda info: Flask("testappgroup")) - runner = CliRunner() result = runner.invoke(cli, ['test'], obj=obj) assert result.exit_code == 0 assert result.output == 'testappgroup\n' @@ -153,7 +156,7 @@ def test_appgroup(): assert result.output == 'testappgroup\n' -def test_flaskgroup(): +def test_flaskgroup(runner): """Test FlaskGroup.""" def create_app(info): return Flask("flaskgroup") @@ -166,16 +169,14 @@ def test_flaskgroup(): def test(): click.echo(current_app.name) - runner = CliRunner() result = runner.invoke(cli, ['test']) assert result.exit_code == 0 assert result.output == 'flaskgroup\n' class TestRoutes: - def test_no_route(self, monkeypatch): + def test_no_route(self, runner, monkeypatch): monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:noroute_app') - runner = CliRunner() result = runner.invoke(cli, ['routes'], catch_exceptions=False) assert result.exit_code == 0 assert result.output == """\ @@ -184,9 +185,8 @@ Route Endpoint Methods /static/ static HEAD, OPTIONS, GET """ - def test_simple_route(self, monkeypatch): + def test_simple_route(self, runner, monkeypatch): monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:simpleroute_app') - runner = CliRunner() result = runner.invoke(cli, ['routes'], catch_exceptions=False) assert result.exit_code == 0 assert result.output == """\ @@ -196,9 +196,8 @@ Route Endpoint Methods /static/ static HEAD, OPTIONS, GET """ - def test_only_POST_route(self, monkeypatch): + def test_only_POST_route(self, runner, monkeypatch): monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:only_POST_route_app') - runner = CliRunner() result = runner.invoke(cli, ['routes'], catch_exceptions=False) assert result.exit_code == 0 assert result.output == """\ From 516ce59f95a3b5d2fffbcde8abfdf1951e748361 Mon Sep 17 00:00:00 2001 From: Antoine Catton Date: Tue, 28 Jun 2016 17:20:25 +0200 Subject: [PATCH 029/399] Add the ability to combine MethodViews --- flask/views.py | 6 +++++- tests/test_views.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 1 deletion(-) diff --git a/flask/views.py b/flask/views.py index 6e249180..922bb132 100644 --- a/flask/views.py +++ b/flask/views.py @@ -102,12 +102,16 @@ class View(object): return view +def get_methods(cls): + return getattr(cls, 'methods', []) or [] + + class MethodViewType(type): def __new__(cls, name, bases, d): rv = type.__new__(cls, name, bases, d) if 'methods' not in d: - methods = set(rv.methods or []) + methods = set(m for b in bases for m in get_methods(b)) for key in d: if key in http_method_funcs: methods.add(key.upper()) diff --git a/tests/test_views.py b/tests/test_views.py index 8a66bd53..65981dbd 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -160,3 +160,45 @@ def test_endpoint_override(): # But these tests should still pass. We just log a warning. common_test(app) + +def test_multiple_inheritance(): + app = flask.Flask(__name__) + + class GetView(flask.views.MethodView): + def get(self): + return 'GET' + + class DeleteView(flask.views.MethodView): + def delete(self): + return 'DELETE' + + class GetDeleteView(GetView, DeleteView): + pass + + app.add_url_rule('/', view_func=GetDeleteView.as_view('index')) + + c = app.test_client() + assert c.get('/').data == b'GET' + assert c.delete('/').data == b'DELETE' + assert sorted(GetDeleteView.methods) == ['DELETE', 'GET'] + +def test_remove_method_from_parent(): + app = flask.Flask(__name__) + + class GetView(flask.views.MethodView): + def get(self): + return 'GET' + + class OtherView(flask.views.MethodView): + def post(self): + return 'POST' + + class View(GetView, OtherView): + methods = ['GET'] + + app.add_url_rule('/', view_func=View.as_view('index')) + + c = app.test_client() + assert c.get('/').data == b'GET' + assert c.post('/').status_code == 405 + assert sorted(View.methods) == ['GET'] From 0c459762ea15960886fd0c2960382212c280eee3 Mon Sep 17 00:00:00 2001 From: John Still Date: Sat, 2 Jul 2016 17:03:36 -0500 Subject: [PATCH 030/399] clarify blueprint 404 error handling in docs --- docs/blueprints.rst | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/docs/blueprints.rst b/docs/blueprints.rst index 89d3701e..98a3d630 100644 --- a/docs/blueprints.rst +++ b/docs/blueprints.rst @@ -177,11 +177,11 @@ the `template_folder` parameter to the :class:`Blueprint` constructor:: admin = Blueprint('admin', __name__, template_folder='templates') For static files, the path can be absolute or relative to the blueprint -resource folder. +resource folder. -The template folder is added to the search path of templates but with a lower -priority than the actual application's template folder. That way you can -easily override templates that a blueprint provides in the actual application. +The template folder is added to the search path of templates but with a lower +priority than the actual application's template folder. That way you can +easily override templates that a blueprint provides in the actual application. This also means that if you don't want a blueprint template to be accidentally overridden, make sure that no other blueprint or actual application template has the same relative path. When multiple blueprints provide the same relative @@ -194,7 +194,7 @@ want to render the template ``'admin/index.html'`` and you have provided this: :file:`yourapplication/admin/templates/admin/index.html`. The reason for the extra ``admin`` folder is to avoid getting our template overridden by a template named ``index.html`` in the actual application template -folder. +folder. To further reiterate this: if you have a blueprint named ``admin`` and you want to render a template called :file:`index.html` which is specific to this @@ -245,4 +245,22 @@ Here is an example for a "404 Page Not Found" exception:: def page_not_found(e): return render_template('pages/404.html') +Most errorhandlers will simply work as expected; however, there is a caveat +concerning handlers for 404 and 405 exceptions. These errorhandlers are only +invoked from an appropriate ``raise`` statement or a call to ``abort`` in another +of the blueprint's view functions; they are not invoked by, e.g., an invalid URL +access. This is because the blueprint does not "own" a certain URL space, so +the application instance has no way of knowing which blueprint errorhandler it +should run if given an invalid URL. If you would like to execute different +handling strategies for these errors based on URL prefixes, they may be defined +at the application level using the ``request`` proxy object:: + + @app.errorhandler(404) + @app.errorhandler(405) + def _handle_api_error(ex): + if request.path.startswith('/api/'): + return jsonify_error(ex) + else: + return ex + More information on error handling see :ref:`errorpages`. From 55f9af72e35449718d405cc140c4705c12239ff2 Mon Sep 17 00:00:00 2001 From: Ioan Vancea Date: Thu, 28 Jul 2016 16:34:48 +0200 Subject: [PATCH 031/399] Added a missing module to import statement --- docs/patterns/deferredcallbacks.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/deferredcallbacks.rst b/docs/patterns/deferredcallbacks.rst index 886ae40a..a79dc913 100644 --- a/docs/patterns/deferredcallbacks.rst +++ b/docs/patterns/deferredcallbacks.rst @@ -60,7 +60,7 @@ At any time during a request, we can register a function to be called at the end of the request. For example you can remember the current language of the user in a cookie in the before-request function:: - from flask import request + from flask import request, after_this_request @app.before_request def detect_user_language(): From c4139e0e5de5ffa8f103956bdcc1ca7cf9e500fd Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Sun, 29 Mar 2015 22:05:32 +0100 Subject: [PATCH 032/399] JSON support for the Flask test client --- flask/testing.py | 15 ++++++++++++++- tests/test_testing.py | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/flask/testing.py b/flask/testing.py index 8eacf58b..dd879c37 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -13,6 +13,7 @@ from contextlib import contextmanager from werkzeug.test import Client, EnvironBuilder from flask import _request_ctx_stack +from flask.json import dumps as json_dumps try: from werkzeug.urls import url_parse @@ -20,7 +21,7 @@ except ImportError: from urlparse import urlsplit as url_parse -def make_test_environ_builder(app, path='/', base_url=None, *args, **kwargs): +def make_test_environ_builder(app, path='/', base_url=None, json=None, *args, **kwargs): """Creates a new test builder with some application defaults thrown in.""" http_host = app.config.get('SERVER_NAME') app_root = app.config.get('APPLICATION_ROOT') @@ -33,6 +34,18 @@ def make_test_environ_builder(app, path='/', base_url=None, *args, **kwargs): path = url.path if url.query: path += '?' + url.query + + if json: + if 'data' in kwargs: + raise RuntimeError('Client cannot provide both `json` and `data`') + kwargs['data'] = json_dumps(json) + + # Only set Content-Type when not explicitly provided + if 'Content-Type' not in kwargs.get('headers', {}): + new_headers = kwargs.get('headers', {}).copy() + new_headers['Content-Type'] = 'application/json' + kwargs['headers'] = new_headers + return EnvironBuilder(path, base_url, *args, **kwargs) diff --git a/tests/test_testing.py b/tests/test_testing.py index 7bb99e79..b3f4defe 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -203,6 +203,20 @@ def test_full_url_request(): assert 'gin' in flask.request.form assert 'vodka' in flask.request.args +def test_json_request(): + app = flask.Flask(__name__) + app.testing = True + + @app.route('/api', methods=['POST']) + def api(): + return '' + + with app.test_client() as c: + json_data = {'drink': {'gin': 1, 'tonic': True}, 'price': 10} + rv = c.post('/api', json=json_data) + assert rv.status_code == 200 + assert flask.request.get_json() == json_data + def test_subdomain(): app = flask.Flask(__name__) app.config['SERVER_NAME'] = 'example.com' From 6c5ef2bc5c7c304c8d4a2ddbb8666167ae73d4c4 Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Sun, 29 Mar 2015 23:05:40 +0100 Subject: [PATCH 033/399] Use `content_type` kwarg instead of manipulating headers --- flask/testing.py | 8 +++----- tests/test_testing.py | 1 + 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/flask/testing.py b/flask/testing.py index dd879c37..3b2f26ef 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -35,16 +35,14 @@ def make_test_environ_builder(app, path='/', base_url=None, json=None, *args, ** if url.query: path += '?' + url.query - if json: + if json is not None: if 'data' in kwargs: raise RuntimeError('Client cannot provide both `json` and `data`') kwargs['data'] = json_dumps(json) # Only set Content-Type when not explicitly provided - if 'Content-Type' not in kwargs.get('headers', {}): - new_headers = kwargs.get('headers', {}).copy() - new_headers['Content-Type'] = 'application/json' - kwargs['headers'] = new_headers + if 'content_type' not in kwargs: + kwargs['content_type'] = 'application/json' return EnvironBuilder(path, base_url, *args, **kwargs) diff --git a/tests/test_testing.py b/tests/test_testing.py index b3f4defe..727f08f7 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -215,6 +215,7 @@ def test_json_request(): json_data = {'drink': {'gin': 1, 'tonic': True}, 'price': 10} rv = c.post('/api', json=json_data) assert rv.status_code == 200 + assert flask.request.is_json assert flask.request.get_json() == json_data def test_subdomain(): From b099999c6cb5c3563dc4fbe45f3101116aab0d3f Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Wed, 1 Apr 2015 00:11:52 +0100 Subject: [PATCH 034/399] Use proper exception type and update changelog --- AUTHORS | 1 + CHANGES | 3 +++ flask/testing.py | 2 +- 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/AUTHORS b/AUTHORS index cc157dc4..b4d746f5 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,6 +9,7 @@ Development Lead Patches and Suggestions ``````````````````````` +- Adam Byrtek - Adam Zapletal - Ali Afshar - Chris Edgemon diff --git a/CHANGES b/CHANGES index 9e13bd71..ea268229 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,9 @@ Version 0.12 See pull request ``#1849``. - Make ``flask.safe_join`` able to join multiple paths like ``os.path.join`` (pull request ``#1730``). +- Added `json` keyword argument to :meth:`flask.testing.FlaskClient.open` + (and related ``get``, ``post``, etc.), which makes it more convenient to + send JSON requests from the test client. Version 0.11.2 -------------- diff --git a/flask/testing.py b/flask/testing.py index 3b2f26ef..6527c8b4 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -37,7 +37,7 @@ def make_test_environ_builder(app, path='/', base_url=None, json=None, *args, ** if json is not None: if 'data' in kwargs: - raise RuntimeError('Client cannot provide both `json` and `data`') + raise ValueError('Client cannot provide both `json` and `data`') kwargs['data'] = json_dumps(json) # Only set Content-Type when not explicitly provided From ca547f0ec375a8a3e8c623cfa6b747d68e75a239 Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Wed, 1 Apr 2015 23:08:06 +0100 Subject: [PATCH 035/399] JSON response tests and first draft of code that passes --- flask/wrappers.py | 81 +++++++++++++++++++++++++++++++++++++++---- tests/test_testing.py | 19 ++++++---- 2 files changed, 88 insertions(+), 12 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index d1d7ba7d..fcf732e3 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -25,6 +25,14 @@ def _get_data(req, cache): return req.data +def _is_mimetype_json(mimetype): + if mimetype == 'application/json': + return True + if mimetype.startswith('application/') and mimetype.endswith('+json'): + return True + return False + + class Request(RequestBase): """The request object used by default in Flask. Remembers the matched endpoint and view arguments. @@ -115,12 +123,7 @@ class Request(RequestBase): .. versionadded:: 0.11 """ - mt = self.mimetype - if mt == 'application/json': - return True - if mt.startswith('application/') and mt.endswith('+json'): - return True - return False + return _is_mimetype_json(self.mimetype) def get_json(self, force=False, silent=False, cache=True): """Parses the incoming JSON request data and returns it. By default @@ -202,3 +205,69 @@ class Response(ResponseBase): set :attr:`~flask.Flask.response_class` to your subclass. """ default_mimetype = 'text/html' + + @property + def json(self): + """If the mimetype is :mimetype:`application/json` this will contain the + parsed JSON data. Otherwise this will be ``None``. + + The :meth:`get_json` method should be used instead. + + .. versionadded:: 1.0 + """ + from warnings import warn + warn(DeprecationWarning('json is deprecated. ' + 'Use get_json() instead.'), stacklevel=2) + return self.get_json() + + @property + def is_json(self): + """Indicates if this response is JSON or not. By default a response + is considered to include JSON data if the mimetype is + :mimetype:`application/json` or :mimetype:`application/*+json`. + + .. versionadded:: 1.0 + """ + return _is_mimetype_json(self.mimetype) + + 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 + request object will be invoked. By default this function will + only load the json data if the mimetype is :mimetype:`application/json` + 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 + and return ``None``. + :param cache: if set to ``True`` the parsed JSON data is remembered + on the request. + + .. versionadded:: 1.0 + """ + rv = getattr(self, '_cached_json', _missing) + if rv is not _missing: + return rv + + if not (force or self.is_json): + return None + + # We accept a request charset against the specification as + # certain clients have been using this in the past. This + # fits our general approach of being nice in what we accept + # and strict in what we send out. + request_charset = self.mimetype_params.get('charset') + try: + data = _get_data(self, cache) + if request_charset is not None: + rv = json.loads(data, encoding=request_charset) + else: + rv = json.loads(data) + except ValueError as e: + if silent: + rv = None + else: + rv = self.on_json_loading_failed(e) + if cache: + self._cached_json = rv + return rv diff --git a/tests/test_testing.py b/tests/test_testing.py index 727f08f7..873fe264 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -13,6 +13,7 @@ import pytest import flask from flask._compat import text_type +from flask.json import jsonify def test_environ_defaults_from_config(): @@ -203,21 +204,27 @@ def test_full_url_request(): assert 'gin' in flask.request.form assert 'vodka' in flask.request.args -def test_json_request(): +def test_json_request_and_response(): app = flask.Flask(__name__) app.testing = True - @app.route('/api', methods=['POST']) - def api(): - return '' + @app.route('/echo', methods=['POST']) + def echo(): + return jsonify(flask.request.json) with app.test_client() as c: json_data = {'drink': {'gin': 1, 'tonic': True}, 'price': 10} - rv = c.post('/api', json=json_data) - assert rv.status_code == 200 + rv = c.post('/echo', json=json_data) + + # Request should be in JSON assert flask.request.is_json assert flask.request.get_json() == json_data + # Response should be in JSON + assert rv.status_code == 200 + assert rv.is_json + assert rv.get_json() == json_data + def test_subdomain(): app = flask.Flask(__name__) app.config['SERVER_NAME'] = 'example.com' From c9ef500c5c2bbc7cfcb51e8b599e00248d290fd4 Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Thu, 2 Apr 2015 00:50:08 +0100 Subject: [PATCH 036/399] Mixin for JSON decoding code shared between request/response --- flask/wrappers.py | 218 +++++++++++++++++----------------------------- 1 file changed, 81 insertions(+), 137 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index fcf732e3..bd6aa0f3 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -25,15 +25,88 @@ def _get_data(req, cache): return req.data -def _is_mimetype_json(mimetype): - if mimetype == 'application/json': - return True - if mimetype.startswith('application/') and mimetype.endswith('+json'): - return True - return False +class JSONMixin(object): + """Mixin for both request and response classes to provide JSON parsing + capabilities. + .. versionadded:: 0.12 + """ + + @property + def is_json(self): + """Indicates if this request/response is JSON or not. By default it + is considered to include JSON data if the mimetype is + :mimetype:`application/json` or :mimetype:`application/*+json`. + """ + mt = self.mimetype + if mt == 'application/json': + return True + if mt.startswith('application/') and mt.endswith('+json'): + return True + return False + + @property + def json(self): + """If the mimetype is :mimetype:`application/json` this will contain the + parsed JSON data. Otherwise this will be ``None``. + + The :meth:`get_json` method should be used instead. + """ + from warnings import warn + warn(DeprecationWarning('json is deprecated. ' + 'Use get_json() instead.'), stacklevel=2) + return self.get_json() + + def get_json(self, force=False, silent=False, cache=True): + """Parses the incoming JSON request data and returns it. By default + this function will return ``None`` if the mimetype is not + :mimetype:`application/json` but this can be overridden by the + ``force`` parameter. If parsing fails the + :meth:`on_json_loading_failed` method on the request object will be + invoked. + + :param force: if set to ``True`` the mimetype is ignored. + :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. + """ + rv = getattr(self, '_cached_json', _missing) + if rv is not _missing: + return rv + + if not (force or self.is_json): + return None + + # We accept a request charset against the specification as certain + # clients have been using this in the past. For responses, we assume + # that if the response charset was set explicitly then the data had + # been encoded correctly as well. + charset = self.mimetype_params.get('charset') + try: + data = _get_data(self, cache) + if charset is not None: + rv = json.loads(data, encoding=charset) + else: + rv = json.loads(data) + except ValueError as e: + if silent: + rv = None + else: + rv = self.on_json_loading_failed(e) + if cache: + self._cached_json = rv + return rv + + def on_json_loading_failed(self, e): + """Called if decoding of the JSON data failed. The return value of + this method is used by :meth:`get_json` when an error occurred. The + default implementation just raises a :class:`BadRequest` exception. + """ + raise BadRequest() -class Request(RequestBase): + +class Request(RequestBase, JSONMixin): """The request object used by default in Flask. Remembers the matched endpoint and view arguments. @@ -103,69 +176,6 @@ class Request(RequestBase): if self.url_rule and '.' in self.url_rule.endpoint: return self.url_rule.endpoint.rsplit('.', 1)[0] - @property - def json(self): - """If the mimetype is :mimetype:`application/json` this will contain the - parsed JSON data. Otherwise this will be ``None``. - - The :meth:`get_json` method should be used instead. - """ - from warnings import warn - warn(DeprecationWarning('json is deprecated. ' - 'Use get_json() instead.'), stacklevel=2) - 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 - :mimetype:`application/json` or :mimetype:`application/*+json`. - - .. versionadded:: 0.11 - """ - return _is_mimetype_json(self.mimetype) - - def get_json(self, force=False, silent=False, cache=True): - """Parses the incoming JSON request data and returns it. By default - this function will return ``None`` if the mimetype is not - :mimetype:`application/json` but this can be overridden by the - ``force`` parameter. If parsing fails the - :meth:`on_json_loading_failed` method on the request object will be - invoked. - - :param force: if set to ``True`` the mimetype is ignored. - :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. - """ - rv = getattr(self, '_cached_json', _missing) - if rv is not _missing: - return rv - - if not (force or self.is_json): - return None - - # We accept a request charset against the specification as - # certain clients have been using this in the past. This - # fits our general approach of being nice in what we accept - # and strict in what we send out. - request_charset = self.mimetype_params.get('charset') - try: - data = _get_data(self, cache) - if request_charset is not None: - rv = json.loads(data, encoding=request_charset) - else: - rv = json.loads(data) - except ValueError as e: - if silent: - rv = None - else: - rv = self.on_json_loading_failed(e) - if cache: - self._cached_json = rv - return rv - def on_json_loading_failed(self, e): """Called if decoding of the JSON data failed. The return value of this method is used by :meth:`get_json` when an error occurred. The @@ -195,7 +205,7 @@ class Request(RequestBase): attach_enctype_error_multidict(self) -class Response(ResponseBase): +class Response(ResponseBase, JSONMixin): """The response object that is used by default in Flask. Works like the response object from Werkzeug but is set to have an HTML mimetype by default. Quite often you don't have to create this object yourself because @@ -205,69 +215,3 @@ class Response(ResponseBase): set :attr:`~flask.Flask.response_class` to your subclass. """ default_mimetype = 'text/html' - - @property - def json(self): - """If the mimetype is :mimetype:`application/json` this will contain the - parsed JSON data. Otherwise this will be ``None``. - - The :meth:`get_json` method should be used instead. - - .. versionadded:: 1.0 - """ - from warnings import warn - warn(DeprecationWarning('json is deprecated. ' - 'Use get_json() instead.'), stacklevel=2) - return self.get_json() - - @property - def is_json(self): - """Indicates if this response is JSON or not. By default a response - is considered to include JSON data if the mimetype is - :mimetype:`application/json` or :mimetype:`application/*+json`. - - .. versionadded:: 1.0 - """ - return _is_mimetype_json(self.mimetype) - - 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 - request object will be invoked. By default this function will - only load the json data if the mimetype is :mimetype:`application/json` - 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 - and return ``None``. - :param cache: if set to ``True`` the parsed JSON data is remembered - on the request. - - .. versionadded:: 1.0 - """ - rv = getattr(self, '_cached_json', _missing) - if rv is not _missing: - return rv - - if not (force or self.is_json): - return None - - # We accept a request charset against the specification as - # certain clients have been using this in the past. This - # fits our general approach of being nice in what we accept - # and strict in what we send out. - request_charset = self.mimetype_params.get('charset') - try: - data = _get_data(self, cache) - if request_charset is not None: - rv = json.loads(data, encoding=request_charset) - else: - rv = json.loads(data) - except ValueError as e: - if silent: - rv = None - else: - rv = self.on_json_loading_failed(e) - if cache: - self._cached_json = rv - return rv From 23de58682c730a67820e77605386a16303d57447 Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Thu, 2 Apr 2015 01:13:48 +0100 Subject: [PATCH 037/399] Remove redundant `cache` flag --- docs/testing.rst | 8 ++++++++ flask/wrappers.py | 6 +++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index fdf57937..90544f7d 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -353,3 +353,11 @@ independently of the session backend used:: Note that in this case you have to use the ``sess`` object instead of the :data:`flask.session` proxy. The object however itself will provide the same interface. + + +Testing JSON APIs +----------------- + +.. versionadded:: 1.0 + +Flask has comprehensive diff --git a/flask/wrappers.py b/flask/wrappers.py index bd6aa0f3..904d50fd 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -18,10 +18,10 @@ from .globals import _request_ctx_stack _missing = object() -def _get_data(req, cache): +def _get_data(req): getter = getattr(req, 'get_data', None) if getter is not None: - return getter(cache=cache) + return getter() return req.data @@ -84,7 +84,7 @@ class JSONMixin(object): # been encoded correctly as well. charset = self.mimetype_params.get('charset') try: - data = _get_data(self, cache) + data = _get_data(self) if charset is not None: rv = json.loads(data, encoding=charset) else: From 539569e5f2074427a99d31a6142cbee596b5905f Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Thu, 2 Apr 2015 01:34:51 +0100 Subject: [PATCH 038/399] Update the testing documentation --- docs/testing.rst | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/testing.rst b/docs/testing.rst index 90544f7d..03a5603d 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -360,4 +360,24 @@ Testing JSON APIs .. versionadded:: 1.0 -Flask has comprehensive +Flask has a great support for JSON, and is a popular choice for building REST +APIs. Testing both JSON requests and responses using the test client is very +convenient: + + from flask import json, jsonify + + @app.route('/api/auth') + def auth(): + email = request.json['email'] + password = request.json['password'] + return jsonify(token=generate_token(email, password)) + + with app.test_client() as c: + email = 'john@example.com' + password = 'secret' + resp = c.post('/api/auth', json={'email': email, 'password': password}) + assert verify_token(email, resp.json['token']) + +Note that if the ``json`` argument is provided then the test client will put +the JSON-serialized object in the request body, and also set the +``Content-Type: application/json`` header. From f0f458e0c5c4610cc210ef80a89b2b0870baa1b6 Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Thu, 2 Apr 2015 01:45:03 +0100 Subject: [PATCH 039/399] Alternative solution for lack of response caching --- flask/wrappers.py | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index 904d50fd..dfba4ed9 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -18,13 +18,6 @@ from .globals import _request_ctx_stack _missing = object() -def _get_data(req): - getter = getattr(req, 'get_data', None) - if getter is not None: - return getter() - return req.data - - class JSONMixin(object): """Mixin for both request and response classes to provide JSON parsing capabilities. @@ -57,6 +50,12 @@ class JSONMixin(object): 'Use get_json() instead.'), stacklevel=2) return self.get_json() + def _get_data_for_json(req, cache): + getter = getattr(req, 'get_data', None) + if getter is not None: + return getter(cache=cache) + return req.data + def get_json(self, force=False, silent=False, cache=True): """Parses the incoming JSON request data and returns it. By default this function will return ``None`` if the mimetype is not @@ -84,7 +83,7 @@ class JSONMixin(object): # been encoded correctly as well. charset = self.mimetype_params.get('charset') try: - data = _get_data(self) + data = self._get_data_for_json(cache) if charset is not None: rv = json.loads(data, encoding=charset) else: @@ -215,3 +214,10 @@ class Response(ResponseBase, JSONMixin): set :attr:`~flask.Flask.response_class` to your subclass. """ default_mimetype = 'text/html' + + def _get_data_for_json(req, cache): + # Ignore the cache flag since response doesn't support it + getter = getattr(req, 'get_data', None) + if getter is not None: + return getter() + return req.data From f0d3b71a94994d52d64d315bc8ef7371db14d76f Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Mon, 6 Apr 2015 13:15:07 +0200 Subject: [PATCH 040/399] Updates after code review --- CHANGES | 2 ++ docs/testing.rst | 16 ++++++++-------- flask/wrappers.py | 14 +++++++------- 3 files changed, 17 insertions(+), 15 deletions(-) diff --git a/CHANGES b/CHANGES index ea268229..a1d0b311 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,8 @@ Version 0.12 - Added `json` keyword argument to :meth:`flask.testing.FlaskClient.open` (and related ``get``, ``post``, etc.), which makes it more convenient to send JSON requests from the test client. +- Added ``is_json`` and ``get_json`` to :class:``flask.wrappers.Response`` + in order to make it easier to build assertions when testing JSON responses. Version 0.11.2 -------------- diff --git a/docs/testing.rst b/docs/testing.rst index 03a5603d..d8c3bac1 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -360,24 +360,24 @@ Testing JSON APIs .. versionadded:: 1.0 -Flask has a great support for JSON, and is a popular choice for building REST +Flask has great support for JSON, and is a popular choice for building REST APIs. Testing both JSON requests and responses using the test client is very convenient: - from flask import json, jsonify + from flask import jsonify @app.route('/api/auth') def auth(): - email = request.json['email'] - password = request.json['password'] + email = request.get_json()['email'] + password = request.get_json()['password'] return jsonify(token=generate_token(email, password)) with app.test_client() as c: email = 'john@example.com' password = 'secret' - resp = c.post('/api/auth', json={'email': email, 'password': password}) - assert verify_token(email, resp.json['token']) + resp = c.post('/api/auth', json={'login': email, 'password': password}) + assert verify_token(email, resp.get_json()['token']) Note that if the ``json`` argument is provided then the test client will put -the JSON-serialized object in the request body, and also set the -``Content-Type: application/json`` header. +JSON-serialized data in the request body, and also set the +``Content-Type: application/json`` HTTP header. diff --git a/flask/wrappers.py b/flask/wrappers.py index dfba4ed9..c25db8b7 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -50,11 +50,11 @@ class JSONMixin(object): 'Use get_json() instead.'), stacklevel=2) return self.get_json() - def _get_data_for_json(req, cache): - getter = getattr(req, 'get_data', None) + def _get_data_for_json(self, cache): + getter = getattr(self, 'get_data', None) if getter is not None: return getter(cache=cache) - return req.data + return self.data def get_json(self, force=False, silent=False, cache=True): """Parses the incoming JSON request data and returns it. By default @@ -215,9 +215,9 @@ class Response(ResponseBase, JSONMixin): """ default_mimetype = 'text/html' - def _get_data_for_json(req, cache): - # Ignore the cache flag since response doesn't support it - getter = getattr(req, 'get_data', None) + def _get_data_for_json(self, cache): + getter = getattr(self, 'get_data', None) if getter is not None: + # Ignore the cache flag since response doesn't support it return getter() - return req.data + return self.data From 1df2788a8f29a1238cbdebd7fa59b0d6218995b5 Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Mon, 6 Apr 2015 13:27:59 +0200 Subject: [PATCH 041/399] Use app_ctx instead of request_ctx to access the app --- flask/wrappers.py | 33 +++++++++++++-------------------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index c25db8b7..ac771650 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -13,7 +13,7 @@ from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase from werkzeug.exceptions import BadRequest from . import json -from .globals import _request_ctx_stack +from .globals import _app_ctx_stack _missing = object() @@ -101,7 +101,17 @@ class JSONMixin(object): """Called if decoding of the JSON data failed. The return value of this method is used by :meth:`get_json` when an error occurred. The default implementation just raises a :class:`BadRequest` exception. + + .. versionchanged:: 0.10 + Removed buggy previous behavior of generating a random JSON + response. If you want that behavior back you can trivially + add it by subclassing. + + .. versionadded:: 0.8 """ + ctx = _app_ctx_stack.top + if ctx is not None and ctx.app.debug: + raise BadRequest('Failed to decode JSON object: {0}'.format(e)) raise BadRequest() @@ -142,7 +152,7 @@ class Request(RequestBase, JSONMixin): @property def max_content_length(self): """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" - ctx = _request_ctx_stack.top + ctx = _app_ctx_stack.top if ctx is not None: return ctx.app.config['MAX_CONTENT_LENGTH'] @@ -175,29 +185,12 @@ class Request(RequestBase, JSONMixin): if self.url_rule and '.' in self.url_rule.endpoint: return self.url_rule.endpoint.rsplit('.', 1)[0] - def on_json_loading_failed(self, e): - """Called if decoding of the JSON data failed. The return value of - this method is used by :meth:`get_json` when an error occurred. The - default implementation just raises a :class:`BadRequest` exception. - - .. versionchanged:: 0.10 - Removed buggy previous behavior of generating a random JSON - response. If you want that behavior back you can trivially - add it by subclassing. - - .. versionadded:: 0.8 - """ - ctx = _request_ctx_stack.top - if ctx is not None and ctx.app.config.get('DEBUG', False): - raise BadRequest('Failed to decode JSON object: {0}'.format(e)) - raise BadRequest() - def _load_form_data(self): RequestBase._load_form_data(self) # 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 + ctx = _app_ctx_stack.top if ctx is not None and ctx.app.debug and \ self.mimetype != 'multipart/form-data' and not self.files: from .debughelpers import attach_enctype_error_multidict From 5575faad92f69993e3823cd94044a2dd7796c82d Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Mon, 6 Apr 2015 13:50:29 +0200 Subject: [PATCH 042/399] Update documentation to use the getter only once --- docs/testing.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index d8c3bac1..8b9af8bd 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -368,15 +368,17 @@ convenient: @app.route('/api/auth') def auth(): - email = request.get_json()['email'] - password = request.get_json()['password'] + json_data = request.get_json() + email = json_data['email'] + password = json_data['password'] return jsonify(token=generate_token(email, password)) with app.test_client() as c: email = 'john@example.com' password = 'secret' resp = c.post('/api/auth', json={'login': email, 'password': password}) - assert verify_token(email, resp.get_json()['token']) + json_data = resp.get_json() + assert verify_token(email, json_data['token']) Note that if the ``json`` argument is provided then the test client will put JSON-serialized data in the request body, and also set the From 5ebdd5dd7455ca4a4c8ba1aff727f40e77a11c08 Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Sun, 12 Apr 2015 23:34:03 +0100 Subject: [PATCH 043/399] Documentation updates --- docs/api.rst | 4 ++-- docs/testing.rst | 4 +++- flask/wrappers.py | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index e72c9ace..9344f7aa 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -29,7 +29,7 @@ Incoming Request Data --------------------- .. autoclass:: Request - :members: + :members: is_json, get_json .. attribute:: form @@ -141,7 +141,7 @@ Response Objects ---------------- .. autoclass:: flask.Response - :members: set_cookie, data, mimetype + :members: set_cookie, data, mimetype, is_json, get_json .. attribute:: headers diff --git a/docs/testing.rst b/docs/testing.rst index 8b9af8bd..07c9aaf5 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -362,7 +362,7 @@ Testing JSON APIs Flask has great support for JSON, and is a popular choice for building REST APIs. Testing both JSON requests and responses using the test client is very -convenient: +convenient:: from flask import jsonify @@ -371,12 +371,14 @@ convenient: json_data = request.get_json() email = json_data['email'] password = json_data['password'] + return jsonify(token=generate_token(email, password)) with app.test_client() as c: email = 'john@example.com' password = 'secret' resp = c.post('/api/auth', json={'login': email, 'password': password}) + json_data = resp.get_json() assert verify_token(email, json_data['token']) diff --git a/flask/wrappers.py b/flask/wrappers.py index ac771650..a91d82c0 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -57,7 +57,7 @@ class JSONMixin(object): return self.data def get_json(self, force=False, silent=False, cache=True): - """Parses the incoming JSON request data and returns it. By default + """Parses the JSON request/response data and returns it. By default this function will return ``None`` if the mimetype is not :mimetype:`application/json` but this can be overridden by the ``force`` parameter. If parsing fails the From 866118302ef910f2b216e23783c9013d282e7807 Mon Sep 17 00:00:00 2001 From: Adam Byrtek Date: Mon, 28 Sep 2015 22:20:11 +0200 Subject: [PATCH 044/399] Remove _missing sentinel and update docs --- flask/wrappers.py | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index a91d82c0..ac181985 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -15,21 +15,21 @@ from werkzeug.exceptions import BadRequest from . import json from .globals import _app_ctx_stack -_missing = object() - class JSONMixin(object): - """Mixin for both request and response classes to provide JSON parsing - capabilities. + """Common mixin for both request and response objects to provide JSON + parsing capabilities. .. versionadded:: 0.12 """ @property def is_json(self): - """Indicates if this request/response is JSON or not. By default it - is considered to include JSON data if the mimetype is + """Indicates if this request/response is in JSON format or not. By + default it is considered to include JSON data if the mimetype is :mimetype:`application/json` or :mimetype:`application/*+json`. + + .. versionadded:: 1.0 """ mt = self.mimetype if mt == 'application/json': @@ -40,14 +40,14 @@ class JSONMixin(object): @property def json(self): - """If the mimetype is :mimetype:`application/json` this will contain the - parsed JSON data. Otherwise this will be ``None``. + """If this request/response is in JSON format then this property will + contain the parsed JSON data. Otherwise it will be ``None``. The :meth:`get_json` method should be used instead. """ from warnings import warn - warn(DeprecationWarning('json is deprecated. ' - 'Use get_json() instead.'), stacklevel=2) + warn(DeprecationWarning( + 'json is deprecated. Use get_json() instead.'), stacklevel=2) return self.get_json() def _get_data_for_json(self, cache): @@ -68,16 +68,17 @@ class JSONMixin(object): :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. + on the object. """ - rv = getattr(self, '_cached_json', _missing) - if rv is not _missing: - return rv + try: + return getattr(self, '_cached_json') + except AttributeError: + pass if not (force or self.is_json): return None - # We accept a request charset against the specification as certain + # We accept MIME charset header against the specification as certain # clients have been using this in the past. For responses, we assume # that if the response charset was set explicitly then the data had # been encoded correctly as well. From 62b0b6652aab03fcf7c411de4d2cbd242ed05ec9 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 19 Aug 2016 21:24:07 +0200 Subject: [PATCH 045/399] testing: Make json a keyword arg --- flask/testing.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flask/testing.py b/flask/testing.py index 6527c8b4..34b66600 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -21,7 +21,7 @@ except ImportError: from urlparse import urlsplit as url_parse -def make_test_environ_builder(app, path='/', base_url=None, json=None, *args, **kwargs): +def make_test_environ_builder(app, path='/', base_url=None, *args, **kwargs): """Creates a new test builder with some application defaults thrown in.""" http_host = app.config.get('SERVER_NAME') app_root = app.config.get('APPLICATION_ROOT') @@ -35,10 +35,10 @@ def make_test_environ_builder(app, path='/', base_url=None, json=None, *args, ** if url.query: path += '?' + url.query - if json is not None: + if 'json' in kwargs: if 'data' in kwargs: raise ValueError('Client cannot provide both `json` and `data`') - kwargs['data'] = json_dumps(json) + kwargs['data'] = json_dumps(kwargs['json']) # Only set Content-Type when not explicitly provided if 'content_type' not in kwargs: From 5c4fa7e91cca82f769c11b098114aa6d3322599b Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 19 Aug 2016 21:25:27 +0200 Subject: [PATCH 046/399] Remove already defined method --- flask/wrappers.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index ac181985..cfb02272 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -208,10 +208,3 @@ class Response(ResponseBase, JSONMixin): set :attr:`~flask.Flask.response_class` to your subclass. """ default_mimetype = 'text/html' - - def _get_data_for_json(self, cache): - getter = getattr(self, 'get_data', None) - if getter is not None: - # Ignore the cache flag since response doesn't support it - return getter() - return self.data From 136a833a8d65f2b89c277d9b909fef5f8a4628de Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 19 Aug 2016 21:29:12 +0200 Subject: [PATCH 047/399] Bugfix: EnvironBuilder doesn't take `json` --- flask/testing.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/testing.py b/flask/testing.py index 34b66600..4ecfdd10 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -38,7 +38,7 @@ def make_test_environ_builder(app, path='/', base_url=None, *args, **kwargs): if 'json' in kwargs: if 'data' in kwargs: raise ValueError('Client cannot provide both `json` and `data`') - kwargs['data'] = json_dumps(kwargs['json']) + kwargs['data'] = json_dumps(kwargs.pop('json')) # Only set Content-Type when not explicitly provided if 'content_type' not in kwargs: From 863e5cca1b498fb3170981892e820df97f4200e7 Mon Sep 17 00:00:00 2001 From: Zev Averbach Date: Sat, 20 Aug 2016 17:13:07 -0400 Subject: [PATCH 048/399] added indentation to changed structure --- docs/appcontext.rst | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/appcontext.rst b/docs/appcontext.rst index 44a21785..03db04ce 100644 --- a/docs/appcontext.rst +++ b/docs/appcontext.rst @@ -8,34 +8,34 @@ The Application Context One of the design ideas behind Flask is that there are at least two different “states” in which code is executed: -1. The application setup state, in which the application implicitly is +1. The application setup state, in which the application implicitly is on the module level. -This state starts when the :class:`Flask` object is instantiated, and -it implicitly ends when the first request comes in. While the -application is in this state, a few assumptions are true: + This state starts when the :class:`Flask` object is instantiated, and + it implicitly ends when the first request comes in. While the + application is in this state, a few assumptions are true: -- the programmer can modify the application object safely. -- no request handling happened so far -- you have to have a reference to the application object in order to - modify it, there is no magic proxy that can give you a reference to - the application object you're currently creating or modifying. + - the programmer can modify the application object safely. + - no request handling happened so far + - you have to have a reference to the application object in order to + modify it, there is no magic proxy that can give you a reference to + the application object you're currently creating or modifying. -2. In contrast, in the request handling state, a couple of other rules +2. In contrast, in the request handling state, a couple of other rules exist: -- while a request is active, the context local objects + - while a request is active, the context local objects (:data:`flask.request` and others) point to the current request. -- any code can get hold of these objects at any time. + - any code can get hold of these objects at any time. -3. There is also a third state somewhere in between 'module-level' and +3. There is also a third state somewhere in between 'module-level' and 'request-handling': -Sometimes you are dealing with an application in a way that is similar to -how you interact with applications during request handling, but without -there being an active request. Consider, for instance, that you're -sitting in an interactive Python shell and interacting with the -application, or a command line application. + Sometimes you are dealing with an application in a way that is similar to + how you interact with applications during request handling, but without + there being an active request. Consider, for instance, that you're + sitting in an interactive Python shell and interacting with the + application, or a command line application. The application context is what powers the :data:`~flask.current_app` context local. From 2b03eca1b791c1f88a46b42c83b62214eb0f04c2 Mon Sep 17 00:00:00 2001 From: Geoffrey Bauduin Date: Tue, 11 Oct 2016 15:27:48 +0200 Subject: [PATCH 049/399] Updated Celery pattern The given pattern caused Celery to lose the current Context --- 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 673d953b..548da29b 100644 --- a/docs/patterns/celery.rst +++ b/docs/patterns/celery.rst @@ -44,7 +44,7 @@ This is all that is necessary to properly integrate Celery with Flask:: abstract = True def __call__(self, *args, **kwargs): with app.app_context(): - return TaskBase.__call__(self, *args, **kwargs) + return self.run(*args, **kwargs) celery.Task = ContextTask return celery From fa087c89298e0c91abff45b3ee7033889d2dd5d7 Mon Sep 17 00:00:00 2001 From: Kyle Lawlor Date: Sun, 30 Oct 2016 09:34:49 -0400 Subject: [PATCH 050/399] Fixes import statement in flaskr (#2068) - `from flaskr.flaskr import app` in flaskr/__init__.py causes an import error with Python 2 - The relative import now used works for py2 and py3 --- docs/tutorial/packaging.rst | 2 +- examples/flaskr/flaskr/__init__.py | 2 +- examples/flaskr/setup.cfg | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorial/packaging.rst b/docs/tutorial/packaging.rst index 8db6531e..18f5c9b3 100644 --- a/docs/tutorial/packaging.rst +++ b/docs/tutorial/packaging.rst @@ -55,7 +55,7 @@ into this file, :file:`flaskr/__init__.py`: .. sourcecode:: python - from flaskr import app + from .flaskr import app This import statement brings the application instance into the top-level of the application package. When it is time to run the application, the diff --git a/examples/flaskr/flaskr/__init__.py b/examples/flaskr/flaskr/__init__.py index 14a36539..3f782479 100644 --- a/examples/flaskr/flaskr/__init__.py +++ b/examples/flaskr/flaskr/__init__.py @@ -1 +1 @@ -from flaskr import app \ No newline at end of file +from .flaskr import app \ No newline at end of file diff --git a/examples/flaskr/setup.cfg b/examples/flaskr/setup.cfg index b7e47898..db50667a 100644 --- a/examples/flaskr/setup.cfg +++ b/examples/flaskr/setup.cfg @@ -1,2 +1,2 @@ -[aliases] +[tool:pytest] test=pytest From cb30a3b5628616c6d34c210eb429650f09c75334 Mon Sep 17 00:00:00 2001 From: Clenimar Filemon Date: Mon, 31 Oct 2016 13:41:38 -0300 Subject: [PATCH 051/399] Update docstring for errorhandler() (#2070) --- flask/app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index 59c77a15..68de2b90 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1153,7 +1153,8 @@ class Flask(_PackageBoundObject): that do not necessarily have to be a subclass of the :class:`~werkzeug.exceptions.HTTPException` class. - :param code: the code as integer for the handler + :param code_or_exception: the code as integer for the handler, or + an arbitrary exception """ def decorator(f): self._register_error_handler(None, code_or_exception, f) From 6478d7bb99ef7dcdda19ce3e58c00500ff72e60b Mon Sep 17 00:00:00 2001 From: Alex Kahan Date: Mon, 31 Oct 2016 18:10:27 -0400 Subject: [PATCH 052/399] Adding coverage generation to tox (#2071) * Adding coverage generation to tox * Removing test directory from coverage command * Adding back to pytest command --- .gitignore | 6 ++++++ tox.ini | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 9bf4f063..fb9baf35 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,9 @@ _mailinglist .tox .cache/ .idea/ + +# Coverage reports +htmlcov +.coverage +.coverage.* +*,cover diff --git a/tox.ini b/tox.ini index 28942d3e..57725d0a 100644 --- a/tox.ini +++ b/tox.ini @@ -4,11 +4,12 @@ envlist = {py26,py27,pypy}-{lowest,release,devel}{,-simplejson}, {py33,py34,py35 [testenv] +usedevelop=true commands = - py.test [] - + py.test [] --cov=flask --cov-report html deps= pytest + pytest-cov greenlet lowest: Werkzeug==0.7 From de1652467b0584fe9ee4060bc42101e8255838a3 Mon Sep 17 00:00:00 2001 From: Martijn Pieters Date: Tue, 1 Nov 2016 14:35:17 +0000 Subject: [PATCH 053/399] Remove busy-work. (#2072) It is entirely sufficient to walk the MRO of the exception class, no need to check for classes re-appearing later on, no need to add the MRO of any superclass. * Python refuses point-blank to create a class with a circular MRO. * All classes in a superclass MRO *already* appear in the MRO of the derived type. Re-adding the contents of a superclass MRO is doing double work. --- flask/app.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/flask/app.py b/flask/app.py index 68de2b90..942992dc 100644 --- a/flask/app.py +++ b/flask/app.py @@ -14,7 +14,6 @@ from threading import Lock from datetime import timedelta from itertools import chain from functools import update_wrapper -from collections import deque from werkzeug.datastructures import ImmutableDict from werkzeug.routing import Map, Rule, RequestRedirect, BuildError @@ -1437,24 +1436,13 @@ class Flask(_PackageBoundObject): def find_handler(handler_map): if not handler_map: return - queue = deque(exc_class.__mro__) - # Protect from geniuses who might create circular references in - # __mro__ - done = set() - - while queue: - cls = queue.popleft() - if cls in done: - continue - done.add(cls) + for cls in exc_class.__mro__: handler = handler_map.get(cls) if handler is not None: # cache for next time exc_class is raised handler_map[exc_class] = handler return handler - queue.extend(cls.__mro__) - # try blueprint handlers handler = find_handler(self.error_handler_spec .get(request.blueprint, {}) From 11809bf1d2f8536a9a50942ddc2bd2b64b595199 Mon Sep 17 00:00:00 2001 From: Philippe Ombredanne Date: Tue, 1 Nov 2016 13:11:53 -0700 Subject: [PATCH 054/399] Add license_file to setup.cfg metadata (#2024) Without this, the LICENSE file is never included in the built wheels: this makes it harder for users to comply with the license. With this addition a file LICENSE.txt will be created in the `xxx.dist-info` directory with the content of the `license_file` file, e.g. the top level LICENSE. --- setup.cfg | 3 +++ 1 file changed, 3 insertions(+) diff --git a/setup.cfg b/setup.cfg index 34414b3e..0e97b0ba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -4,5 +4,8 @@ release = egg_info -RDb '' [wheel] universal = 1 +[metadata] +license_file = LICENSE + [tool:pytest] norecursedirs = .* *.egg *.egg-info env* artwork docs examples From 77af942b982a3b07669362cc6bc223026bf039cd Mon Sep 17 00:00:00 2001 From: Clenimar Filemon Date: Tue, 1 Nov 2016 22:52:32 -0300 Subject: [PATCH 055/399] Capitalize occurrences of 'flask' (#2067) --- flask/__init__.py | 2 +- flask/cli.py | 4 ++-- flask/sessions.py | 2 +- flask/signals.py | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/flask/__init__.py b/flask/__init__.py index 509b944f..b1360e84 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -40,7 +40,7 @@ from .signals import signals_available, template_rendered, request_started, \ # it. from . import json -# This was the only thing that flask used to export at one point and it had +# This was the only thing that Flask used to export at one point and it had # a more generic name. jsonify = json.jsonify diff --git a/flask/cli.py b/flask/cli.py index 6c8cf32d..da988e80 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -131,9 +131,9 @@ version_option = click.Option(['--version'], is_flag=True, is_eager=True) class DispatchingApp(object): - """Special application that dispatches to a flask application which + """Special application that dispatches to a Flask application which is imported by name in a background thread. If an error happens - it is is recorded and shows as part of the WSGI handling which in case + it is recorded and shown as part of the WSGI handling which in case of the Werkzeug debugger means that it shows up in the browser. """ diff --git a/flask/sessions.py b/flask/sessions.py index b9120712..4d67658a 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -168,7 +168,7 @@ class SessionInterface(object): null_session_class = NullSession #: A flag that indicates if the session interface is pickle based. - #: This can be used by flask extensions to make a decision in regards + #: This can be used by Flask extensions to make a decision in regards #: to how to deal with the session object. #: #: .. versionadded:: 0.10 diff --git a/flask/signals.py b/flask/signals.py index c9b8a210..dd52cdb5 100644 --- a/flask/signals.py +++ b/flask/signals.py @@ -37,7 +37,7 @@ 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() From 9685d14eaa78de991e80db41e839fe5e932ee95f Mon Sep 17 00:00:00 2001 From: Shandy Brown Date: Tue, 1 Nov 2016 18:52:54 -0700 Subject: [PATCH 056/399] Correct grammar (#2061) --- 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 40e048e0..e8215317 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -135,7 +135,7 @@ Here is an example :file:`database.py` module for your application:: def init_db(): metadata.create_all(bind=engine) -As for the declarative approach you need to close the session after +As in the declarative approach, you need to close the session after each request or application context shutdown. Put this into your application module:: From bbe58a4944d67087b5cf59b54aa31a8834055187 Mon Sep 17 00:00:00 2001 From: Tery Lim Date: Wed, 2 Nov 2016 15:58:22 +1300 Subject: [PATCH 057/399] Update errorhandling.rst (#2074) --- docs/errorhandling.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst index 3bda5f15..18493c67 100644 --- a/docs/errorhandling.rst +++ b/docs/errorhandling.rst @@ -216,7 +216,7 @@ A formatter can be instantiated with a format string. Note that tracebacks are appended to the log entry automatically. You don't have to do that in the log formatter format string. -Here some example setups: +Here are some example setups: Email ````` From ec9717502f36126b8a371b73a5730b2092948ca2 Mon Sep 17 00:00:00 2001 From: Tery Lim Date: Wed, 2 Nov 2016 17:04:20 +1300 Subject: [PATCH 058/399] Update errorhandling.rst (#2075) Fix LogRecord class reference. --- docs/errorhandling.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst index 18493c67..64c0f8b3 100644 --- a/docs/errorhandling.rst +++ b/docs/errorhandling.rst @@ -276,8 +276,9 @@ that this list is not complete, consult the official documentation of the | ``%(lineno)d`` | Source line number where the logging call was | | | issued (if available). | +------------------+----------------------------------------------------+ -| ``%(asctime)s`` | Human-readable time when the LogRecord` was | -| | created. By default this is of the form | +| ``%(asctime)s`` | Human-readable time when the | +| | :class:`~logging.LogRecord` was created. | +| | By default this is of the form | | | ``"2003-07-08 16:49:45,896"`` (the numbers after | | | the comma are millisecond portion of the time). | | | This can be changed by subclassing the formatter | From a4ed3d28066bb1625c15fc6a89c1533535dc7879 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Wed, 2 Nov 2016 17:56:59 +0100 Subject: [PATCH 059/399] Use tox from make test --- Makefile | 7 ++----- setup.cfg | 2 +- test-requirements.txt | 2 +- tox.ini | 6 +++++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Makefile b/Makefile index 9bcdebc2..f76c2099 100644 --- a/Makefile +++ b/Makefile @@ -3,11 +3,8 @@ all: clean-pyc test test: - pip install -r test-requirements.txt -q - FLASK_DEBUG= py.test tests examples - -tox-test: - tox + pip install -r test-requirements.txt + tox -e py-release audit: python setup.py audit diff --git a/setup.cfg b/setup.cfg index 0e97b0ba..cd282e48 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,4 +8,4 @@ universal = 1 license_file = LICENSE [tool:pytest] -norecursedirs = .* *.egg *.egg-info env* artwork docs examples +norecursedirs = .* *.egg *.egg-info env* artwork docs diff --git a/test-requirements.txt b/test-requirements.txt index e079f8a6..053148f8 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -1 +1 @@ -pytest +tox diff --git a/tox.ini b/tox.ini index 57725d0a..406de5dd 100644 --- a/tox.ini +++ b/tox.ini @@ -4,9 +4,13 @@ envlist = {py26,py27,pypy}-{lowest,release,devel}{,-simplejson}, {py33,py34,py35 [testenv] +passenv = LANG usedevelop=true commands = - py.test [] --cov=flask --cov-report html + # We need to install those after Flask is installed. + pip install -e examples/flaskr + pip install -e examples/minitwit + py.test --cov=flask --cov-report html [] deps= pytest pytest-cov From 2647fc7112ed79c1751a3d99491d7dcfe0aa4520 Mon Sep 17 00:00:00 2001 From: Alex Kahan Date: Thu, 3 Nov 2016 13:11:24 -0400 Subject: [PATCH 060/399] Parameterizing test (#2073) --- tests/test_helpers.py | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 8348331b..69fbaf3b 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -113,20 +113,17 @@ class TestJSON(object): rv = flask.json.load(out) assert rv == test_data - def test_jsonify_basic_types(self): + @pytest.mark.parametrize('test_value', [0, -1, 1, 23, 3.14, 's', "longer string", True, False, None]) + def test_jsonify_basic_types(self, test_value): """Test jsonify with basic types.""" - # Should be able to use pytest parametrize on this, but I couldn't - # figure out the correct syntax - # https://pytest.org/latest/parametrize.html#pytest-mark-parametrize-parametrizing-test-functions - test_data = (0, 1, 23, 3.14, 's', "longer string", True, False,) app = flask.Flask(__name__) c = app.test_client() - for i, d in enumerate(test_data): - url = '/jsonify_basic_types{0}'.format(i) - app.add_url_rule(url, str(i), lambda x=d: flask.jsonify(x)) - rv = c.get(url) - assert rv.mimetype == 'application/json' - assert flask.json.loads(rv.data) == d + + url = '/jsonify_basic_types' + app.add_url_rule(url, url, lambda x=test_value: flask.jsonify(x)) + rv = c.get(url) + assert rv.mimetype == 'application/json' + assert flask.json.loads(rv.data) == test_value def test_jsonify_dicts(self): """Test jsonify with dicts and kwargs unpacking.""" @@ -170,12 +167,10 @@ class TestJSON(object): def test_jsonify_date_types(self): """Test jsonify with datetime.date and datetime.datetime types.""" - test_dates = ( datetime.datetime(1973, 3, 11, 6, 30, 45), datetime.date(1975, 1, 5) ) - app = flask.Flask(__name__) c = app.test_client() @@ -189,8 +184,7 @@ class TestJSON(object): def test_jsonify_uuid_types(self): """Test jsonify with uuid.UUID types""" - test_uuid = uuid.UUID(bytes=b'\xDE\xAD\xBE\xEF'*4) - + test_uuid = uuid.UUID(bytes=b'\xDE\xAD\xBE\xEF' * 4) app = flask.Flask(__name__) url = '/uuid_test' app.add_url_rule(url, url, lambda: flask.jsonify(x=test_uuid)) From 08d6de73a893615ef316a9ad88710bd7e6589bf2 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 3 Nov 2016 23:01:10 -0700 Subject: [PATCH 061/399] switch to packaged sphinx theme --- .gitmodules | 3 -- docs/_themes | 1 - docs/conf.py | 14 +++----- docs/flaskext.py | 86 ------------------------------------------------ 4 files changed, 4 insertions(+), 100 deletions(-) delete mode 100644 .gitmodules delete mode 160000 docs/_themes delete mode 100644 docs/flaskext.py diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 3d7df149..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "docs/_themes"] - path = docs/_themes - url = https://github.com/mitsuhiko/flask-sphinx-themes.git diff --git a/docs/_themes b/docs/_themes deleted file mode 160000 index 3d964b66..00000000 --- a/docs/_themes +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3d964b660442e23faedf801caed6e3c7bd42d5c9 diff --git a/docs/conf.py b/docs/conf.py index b37427a8..111a9089 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -19,7 +19,6 @@ import pkg_resources # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append(os.path.join(os.path.dirname(__file__), '_themes')) sys.path.append(os.path.dirname(__file__)) # -- General configuration ----------------------------------------------------- @@ -110,7 +109,7 @@ exclude_patterns = ['_build'] # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -html_theme_path = ['_themes'] +# html_theme_path = ['_themes'] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". @@ -263,19 +262,14 @@ intersphinx_mapping = { } try: - __import__('flask_theme_support') - pygments_style = 'flask_theme_support.FlaskyStyle' + __import__('flask_sphinx_themes') html_theme = 'flask' html_theme_options = { 'touch_icon': 'touch-icon.png' } except ImportError: - print('-' * 74) - print('Warning: Flask themes unavailable. Building with default theme') - print('If you want the Flask themes, run this command and build again:') - print() - print(' git submodule update --init') - print('-' * 74) + print('Flask theme unavailable; using the default theme.') + print('Install using `pip install Flask-Sphinx-Themes`.') # unwrap decorators diff --git a/docs/flaskext.py b/docs/flaskext.py deleted file mode 100644 index 33f47449..00000000 --- a/docs/flaskext.py +++ /dev/null @@ -1,86 +0,0 @@ -# flasky extensions. flasky pygments style based on tango style -from pygments.style import Style -from pygments.token import Keyword, Name, Comment, String, Error, \ - Number, Operator, Generic, Whitespace, Punctuation, Other, Literal - - -class FlaskyStyle(Style): - background_color = "#f8f8f8" - default_style = "" - - styles = { - # No corresponding class for the following: - #Text: "", # class: '' - Whitespace: "underline #f8f8f8", # class: 'w' - Error: "#a40000 border:#ef2929", # class: 'err' - Other: "#000000", # class 'x' - - Comment: "italic #8f5902", # class: 'c' - Comment.Preproc: "noitalic", # class: 'cp' - - Keyword: "bold #004461", # class: 'k' - Keyword.Constant: "bold #004461", # class: 'kc' - Keyword.Declaration: "bold #004461", # class: 'kd' - Keyword.Namespace: "bold #004461", # class: 'kn' - Keyword.Pseudo: "bold #004461", # class: 'kp' - Keyword.Reserved: "bold #004461", # class: 'kr' - Keyword.Type: "bold #004461", # class: 'kt' - - Operator: "#582800", # class: 'o' - Operator.Word: "bold #004461", # class: 'ow' - like keywords - - Punctuation: "bold #000000", # class: 'p' - - # because special names such as Name.Class, Name.Function, etc. - # are not recognized as such later in the parsing, we choose them - # to look the same as ordinary variables. - Name: "#000000", # class: 'n' - Name.Attribute: "#c4a000", # class: 'na' - to be revised - Name.Builtin: "#004461", # class: 'nb' - Name.Builtin.Pseudo: "#3465a4", # class: 'bp' - Name.Class: "#000000", # class: 'nc' - to be revised - Name.Constant: "#000000", # class: 'no' - to be revised - Name.Decorator: "#888", # class: 'nd' - to be revised - Name.Entity: "#ce5c00", # class: 'ni' - Name.Exception: "bold #cc0000", # class: 'ne' - Name.Function: "#000000", # class: 'nf' - Name.Property: "#000000", # class: 'py' - Name.Label: "#f57900", # class: 'nl' - Name.Namespace: "#000000", # class: 'nn' - to be revised - Name.Other: "#000000", # class: 'nx' - Name.Tag: "bold #004461", # class: 'nt' - like a keyword - Name.Variable: "#000000", # class: 'nv' - to be revised - Name.Variable.Class: "#000000", # class: 'vc' - to be revised - Name.Variable.Global: "#000000", # class: 'vg' - to be revised - Name.Variable.Instance: "#000000", # class: 'vi' - to be revised - - Number: "#990000", # class: 'm' - - Literal: "#000000", # class: 'l' - Literal.Date: "#000000", # class: 'ld' - - String: "#4e9a06", # class: 's' - String.Backtick: "#4e9a06", # class: 'sb' - String.Char: "#4e9a06", # class: 'sc' - String.Doc: "italic #8f5902", # class: 'sd' - like a comment - String.Double: "#4e9a06", # class: 's2' - String.Escape: "#4e9a06", # class: 'se' - String.Heredoc: "#4e9a06", # class: 'sh' - String.Interpol: "#4e9a06", # class: 'si' - String.Other: "#4e9a06", # class: 'sx' - String.Regex: "#4e9a06", # class: 'sr' - String.Single: "#4e9a06", # class: 's1' - String.Symbol: "#4e9a06", # class: 'ss' - - Generic: "#000000", # class: 'g' - Generic.Deleted: "#a40000", # class: 'gd' - Generic.Emph: "italic #000000", # class: 'ge' - Generic.Error: "#ef2929", # class: 'gr' - Generic.Heading: "bold #000080", # class: 'gh' - Generic.Inserted: "#00A000", # class: 'gi' - Generic.Output: "#888", # class: 'go' - Generic.Prompt: "#745334", # class: 'gp' - Generic.Strong: "bold #000000", # class: 'gs' - Generic.Subheading: "bold #800080", # class: 'gu' - Generic.Traceback: "bold #a40000", # class: 'gt' - } From 281c9c3ff9f4a49960c07b23cad4554a5c349272 Mon Sep 17 00:00:00 2001 From: Giles Thomas Date: Mon, 7 Nov 2016 18:10:02 +0000 Subject: [PATCH 062/399] Added a link to instructions for PythonAnywhere (#2081) --- docs/deploying/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst index 5d88cf72..95e96bf2 100644 --- a/docs/deploying/index.rst +++ b/docs/deploying/index.rst @@ -23,6 +23,7 @@ Hosted options - `Deploying Flask on Google App Engine `_ - `Sharing your Localhost Server with Localtunnel `_ - `Deploying on Azure (IIS) `_ +- `Deploying on PythonAnywhere `_ Self-hosted options ------------------- From 4cf4229355459ecb47b0f862302848ce92a5bbbc Mon Sep 17 00:00:00 2001 From: Jannis Leidel Date: Tue, 15 Nov 2016 11:57:09 +0100 Subject: [PATCH 063/399] Fix rST rendering of env var (#2085) This was broken in https://github.com/pallets/flask/commit/ad011bc32d7b9160354efafcd43e20f7042a6a13#diff-fd40cf2be7711772de9d8316da038cceR263 --- docs/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 89fa0924..6d37c1e8 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -262,7 +262,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`` +with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS` 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 7e1a13ffbd212e928e866c2e4cf5d92d85660f86 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Tue, 15 Nov 2016 11:56:51 +0100 Subject: [PATCH 064/399] Fix import error --- examples/minitwit/minitwit/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/minitwit/minitwit/__init__.py b/examples/minitwit/minitwit/__init__.py index 0b8bd697..96c81aec 100644 --- a/examples/minitwit/minitwit/__init__.py +++ b/examples/minitwit/minitwit/__init__.py @@ -1 +1 @@ -from minitwit import app \ No newline at end of file +from .minitwit import app From 4a8bf651d9177fbe745fddd1aa2ab098b0670437 Mon Sep 17 00:00:00 2001 From: ezramorris Date: Thu, 17 Nov 2016 14:01:30 +0000 Subject: [PATCH 065/399] Add link to AWS EB Flask tutorial --- docs/deploying/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst index 95e96bf2..20e71762 100644 --- a/docs/deploying/index.rst +++ b/docs/deploying/index.rst @@ -21,6 +21,7 @@ Hosted options - `Deploying Flask on OpenShift `_ - `Deploying Flask on Webfaction `_ - `Deploying Flask on Google App Engine `_ +- `Deploying Flask on AWS Elastic Beanstalk `_ - `Sharing your Localhost Server with Localtunnel `_ - `Deploying on Azure (IIS) `_ - `Deploying on PythonAnywhere `_ From ccb562854efad180bcbd37f126757d493ed68ff3 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Mon, 19 Dec 2016 14:37:34 +0100 Subject: [PATCH 066/399] Remove wrong comma (#2116) --- docs/patterns/appfactories.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index dc9660ae..c118a273 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -6,7 +6,7 @@ Application Factories If you are already using packages and blueprints for your application (:ref:`blueprints`) there are a couple of really nice ways to further improve the experience. A common pattern is creating the application object when -the blueprint is imported. But if you move the creation of this object, +the blueprint is imported. But if you move the creation of this object into a function, you can then create multiple instances of this app later. So why would you want to do this? From 0ba1a872b7aac48efef9028e7687475da34eaa39 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Wed, 21 Dec 2016 21:06:48 +0100 Subject: [PATCH 067/399] Style the flask command consistently (#2120) It's done like this in other parts of this doc. --- docs/cli.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 7ddf50f3..2ca0e83e 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -139,8 +139,8 @@ This could be a file named :file:`autoapp.py` with these contents:: 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:: +Once this has happened you can make the :command:`flask` command automatically +pick it up:: export YOURAPPLICATION_CONFIG=/path/to/config.cfg export FLASK_APP=/path/to/autoapp.py From 7f288371674b420ac2df8ff2313e3b469662ee41 Mon Sep 17 00:00:00 2001 From: Hopsken Date: Thu, 22 Dec 2016 04:07:09 +0800 Subject: [PATCH 068/399] Update README for minitwit (#2119) add step 2 to run minitwit --- examples/minitwit/README | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/minitwit/README b/examples/minitwit/README index 4561d836..b9bc5ea2 100644 --- a/examples/minitwit/README +++ b/examples/minitwit/README @@ -14,15 +14,19 @@ export an MINITWIT_SETTINGS environment variable pointing to a configuration file. - 2. tell flask about the right application: + 2. install the app from the root of the project directory + + pip install --editable . + + 3. tell flask about the right application: export FLASK_APP=minitwit - 2. fire up a shell and run this: + 4. fire up a shell and run this: flask initdb - 3. now you can run minitwit: + 5. now you can run minitwit: flask run From 0e79aba40d2497218736448ced708fcf4f8943b3 Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Wed, 21 Dec 2016 12:07:57 -0800 Subject: [PATCH 069/399] use dict instead of if/else logic (#2093) --- flask/sessions.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/flask/sessions.py b/flask/sessions.py index 4d67658a..525ff246 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -84,21 +84,25 @@ class TaggedJSONSerializer(object): def dumps(self, value): return json.dumps(_tag(value), separators=(',', ':')) + LOADS_MAP = { + ' t': tuple, + ' u': uuid.UUID, + ' b': b64decode, + ' m': Markup, + ' d': parse_date, + } + def loads(self, value): def object_hook(obj): if len(obj) != 1: return obj the_key, the_value = next(iteritems(obj)) - if the_key == ' t': - return tuple(the_value) - elif the_key == ' u': - return uuid.UUID(the_value) - elif the_key == ' b': - return b64decode(the_value) - elif the_key == ' m': - return Markup(the_value) - elif the_key == ' d': - return parse_date(the_value) + # Check the key for a corresponding function + return_function = self.LOADS_MAP.get(the_key) + if return_function: + # Pass the value to the function + return return_function(the_value) + # Didn't find a function for this object return obj return json.loads(value, object_hook=object_hook) From 36425d5f91b57210f7707de9564377fea93825b2 Mon Sep 17 00:00:00 2001 From: Jiri Kuncar Date: Wed, 21 Dec 2016 21:08:38 +0100 Subject: [PATCH 070/399] Ignore cache on request.get_json(cache=False) call (#2089) * Test cache argument of Request.get_json * Ignore cache on request.get_json(cache=False) call Removes usage of `_cached_json` property when `get_json` is called with disabled cache argument. (closes #2087) --- flask/wrappers.py | 3 ++- tests/test_helpers.py | 8 ++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index d1d7ba7d..04bdcb5d 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -137,7 +137,8 @@ class Request(RequestBase): on the request. """ rv = getattr(self, '_cached_json', _missing) - if rv is not _missing: + # We return cached JSON only when the cache is enabled. + if cache and rv is not _missing: return rv if not (force or self.is_json): diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 69fbaf3b..3e2ea8cd 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -35,6 +35,14 @@ def has_encoding(name): class TestJSON(object): + def test_ignore_cached_json(self): + app = flask.Flask(__name__) + with app.test_request_context('/', method='POST', data='malformed', + content_type='application/json'): + assert flask.request.get_json(silent=True, cache=True) is None + with pytest.raises(BadRequest): + flask.request.get_json(silent=False, cache=False) + def test_post_empty_json_adds_exception_to_response_content_in_debug(self): app = flask.Flask(__name__) app.config['DEBUG'] = True From 45c45ea73c993cd12334194d62e0f83027a46b07 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Wed, 21 Dec 2016 21:19:53 +0100 Subject: [PATCH 071/399] Version 0.12 --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 13ce156c..73e78128 100644 --- a/CHANGES +++ b/CHANGES @@ -6,6 +6,8 @@ Here you can see the full list of changes between each Flask release. Version 0.12 ------------ +Released on December 21st 2016, codename Punsch. + - the cli command now responds to `--version`. - Mimetype guessing and ETag generation for file-like objects in ``send_file`` has been removed, as per issue ``#104``. See pull request ``#1849``. From 1042d9d23f3c61f4474aea568a359337cf450fab Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Wed, 21 Dec 2016 21:22:08 +0100 Subject: [PATCH 072/399] Bump version number to 0.12 --- flask/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__init__.py b/flask/__init__.py index b1360e84..69f29fb4 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.11.2-dev' +__version__ = '0.12' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. From b2e0886f484ac4544afd440955aa936379161e7c Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Wed, 21 Dec 2016 21:22:26 +0100 Subject: [PATCH 073/399] Bump to dev version --- flask/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__init__.py b/flask/__init__.py index 69f29fb4..bb6c4c18 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.12' +__version__ = '0.13-dev' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. From caf6b8c3141bc1c251952600a80690fcb5e11009 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sun, 25 Dec 2016 16:33:55 +0100 Subject: [PATCH 074/399] Changelog stub for 0.12.1 --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index 73e78128..317bd489 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 0.12.1 +-------------- + +Bugfix release, unreleased + Version 0.12 ------------ From 8cd0b03beeac4a41c398ea365475c651c484a9ee Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sun, 25 Dec 2016 16:34:22 +0100 Subject: [PATCH 075/399] Bump to dev 0.12.1 --- flask/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__init__.py b/flask/__init__.py index 69f29fb4..3cef3b43 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.12' +__version__ = '0.12.1-dev' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. From 789715adb9949f58b7b0272bed1a58d7cd0fad30 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Mon, 26 Dec 2016 03:50:47 +0100 Subject: [PATCH 076/399] Fix config.from_pyfile on Python 3 (#2123) * Fix config.from_pyfile on Python 3 Fix #2118 * Support Python 2.6 * Fix tests on Python 2 --- CHANGES | 3 +++ flask/config.py | 2 +- tests/test_config.py | 22 ++++++++++++++++++++-- 3 files changed, 24 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 317bd489..a0a423d1 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,9 @@ Version 0.12.1 Bugfix release, unreleased +- Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix + ``#2118``. + Version 0.12 ------------ diff --git a/flask/config.py b/flask/config.py index 36e8a123..697add71 100644 --- a/flask/config.py +++ b/flask/config.py @@ -126,7 +126,7 @@ class Config(dict): d = types.ModuleType('config') d.__file__ = filename try: - with open(filename) as config_file: + with open(filename, mode='rb') as config_file: exec(compile(config_file.read(), filename, 'exec'), d.__dict__) except IOError as e: if silent and e.errno in (errno.ENOENT, errno.EISDIR): diff --git a/tests/test_config.py b/tests/test_config.py index 333a5cff..5c98db98 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -7,11 +7,14 @@ :license: BSD, see LICENSE for more details. """ -import pytest -import os from datetime import timedelta +import os +import textwrap + import flask +from flask._compat import PY2 +import pytest # config keys used for the TestConfig @@ -187,3 +190,18 @@ def test_get_namespace(): assert 2 == len(bar_options) assert 'bar stuff 1' == bar_options['BAR_STUFF_1'] assert 'bar stuff 2' == bar_options['BAR_STUFF_2'] + + +@pytest.mark.parametrize('encoding', ['utf-8', 'iso-8859-15', 'latin-1']) +def test_from_pyfile_weird_encoding(tmpdir, encoding): + f = tmpdir.join('my_config.py') + f.write_binary(textwrap.dedent(u''' + # -*- coding: {0} -*- + TEST_VALUE = "föö" + '''.format(encoding)).encode(encoding)) + app = flask.Flask(__name__) + app.config.from_pyfile(str(f)) + value = app.config['TEST_VALUE'] + if PY2: + value = value.decode(encoding) + assert value == u'föö' From 079d752ceca6ea28d953198d643c2cdd1f895531 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Lipt=C3=A1k?= Date: Wed, 28 Dec 2016 10:11:33 -0500 Subject: [PATCH 077/399] Update Flask-SQLAlchemy link (#2126) --- 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 e8215317..9c985cc6 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://pythonhosted.org/Flask-SQLAlchemy/ +.. _Flask-SQLAlchemy: http://flask-sqlalchemy.pocoo.org/ Declarative From e49b73d2cf7d08a6f3aa3940b4492ee1f3176787 Mon Sep 17 00:00:00 2001 From: wgwz Date: Fri, 30 Dec 2016 12:43:31 -0500 Subject: [PATCH 078/399] Adds the largerapp from the docs as an example --- examples/largerapp/setup.py | 10 ++++++++++ examples/largerapp/yourapplication/__init__.py | 4 ++++ examples/largerapp/yourapplication/static/style.css | 0 .../largerapp/yourapplication/templates/index.html | 0 .../largerapp/yourapplication/templates/layout.html | 0 .../largerapp/yourapplication/templates/login.html | 0 examples/largerapp/yourapplication/views.py | 5 +++++ 7 files changed, 19 insertions(+) create mode 100644 examples/largerapp/setup.py create mode 100644 examples/largerapp/yourapplication/__init__.py create mode 100644 examples/largerapp/yourapplication/static/style.css create mode 100644 examples/largerapp/yourapplication/templates/index.html create mode 100644 examples/largerapp/yourapplication/templates/layout.html create mode 100644 examples/largerapp/yourapplication/templates/login.html create mode 100644 examples/largerapp/yourapplication/views.py diff --git a/examples/largerapp/setup.py b/examples/largerapp/setup.py new file mode 100644 index 00000000..eaf00f07 --- /dev/null +++ b/examples/largerapp/setup.py @@ -0,0 +1,10 @@ +from setuptools import setup + +setup( + name='yourapplication', + packages=['yourapplication'], + include_package_data=True, + install_requires=[ + 'flask', + ], +) diff --git a/examples/largerapp/yourapplication/__init__.py b/examples/largerapp/yourapplication/__init__.py new file mode 100644 index 00000000..089d2937 --- /dev/null +++ b/examples/largerapp/yourapplication/__init__.py @@ -0,0 +1,4 @@ +from flask import Flask +app = Flask(__name__) + +import yourapplication.views \ No newline at end of file diff --git a/examples/largerapp/yourapplication/static/style.css b/examples/largerapp/yourapplication/static/style.css new file mode 100644 index 00000000..e69de29b diff --git a/examples/largerapp/yourapplication/templates/index.html b/examples/largerapp/yourapplication/templates/index.html new file mode 100644 index 00000000..e69de29b diff --git a/examples/largerapp/yourapplication/templates/layout.html b/examples/largerapp/yourapplication/templates/layout.html new file mode 100644 index 00000000..e69de29b diff --git a/examples/largerapp/yourapplication/templates/login.html b/examples/largerapp/yourapplication/templates/login.html new file mode 100644 index 00000000..e69de29b diff --git a/examples/largerapp/yourapplication/views.py b/examples/largerapp/yourapplication/views.py new file mode 100644 index 00000000..b112328e --- /dev/null +++ b/examples/largerapp/yourapplication/views.py @@ -0,0 +1,5 @@ +from yourapplication import app + +@app.route('/') +def index(): + return 'Hello World!' \ No newline at end of file From 949771adf51ecaea685b57cb37089b4772f9d285 Mon Sep 17 00:00:00 2001 From: wgwz Date: Fri, 30 Dec 2016 12:50:13 -0500 Subject: [PATCH 079/399] Add reference to largerapp src in docs --- docs/patterns/packages.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/patterns/packages.rst b/docs/patterns/packages.rst index 1cd77974..d1780ca8 100644 --- a/docs/patterns/packages.rst +++ b/docs/patterns/packages.rst @@ -114,6 +114,10 @@ You should then end up with something like that:: login.html ... +If you find yourself stuck on something, feel free +to take a look at the source code for this example. +You'll find it located under ``flask/examples/largerapp``. + .. admonition:: Circular Imports Every Python programmer hates them, and yet we just added some: From 582a878ad96b59207931635fa65704c5ab295535 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 30 Dec 2016 22:28:43 +0100 Subject: [PATCH 080/399] Init 0.13 changelog --- CHANGES | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGES b/CHANGES index a0a423d1..91d4813b 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 0.13 +------------ + +Major release, unreleased + Version 0.12.1 -------------- From 0832e77b145b226bac4ee82144221800a0f7d34a Mon Sep 17 00:00:00 2001 From: Paul Brown Date: Fri, 30 Dec 2016 15:02:08 -0600 Subject: [PATCH 081/399] prevent NoAppException when ImportError occurs within imported module --- flask/cli.py | 14 ++++++++++---- tests/test_apps/cliapp/importerrorapp.py | 7 +++++++ tests/test_cli.py | 1 + 3 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 tests/test_apps/cliapp/importerrorapp.py diff --git a/flask/cli.py b/flask/cli.py index da988e80..074ee768 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -89,10 +89,16 @@ def locate_app(app_id): try: __import__(module) except ImportError: - raise NoAppException('The file/path provided (%s) does not appear to ' - 'exist. Please verify the path is correct. If ' - 'app is not on PYTHONPATH, ensure the extension ' - 'is .py' % module) + # Reraise the ImportError if it occurred within the imported module. + # Determine this by checking whether the trace has a depth > 1. + if sys.exc_info()[-1].tb_next: + raise + else: + raise NoAppException('The file/path provided (%s) does not appear' + ' to exist. Please verify the path is ' + 'correct. If app is not on PYTHONPATH, ' + 'ensure the extension is .py' % module) + mod = sys.modules[module] if app_obj is None: app = find_best_app(mod) diff --git a/tests/test_apps/cliapp/importerrorapp.py b/tests/test_apps/cliapp/importerrorapp.py new file mode 100644 index 00000000..fb87c9b1 --- /dev/null +++ b/tests/test_apps/cliapp/importerrorapp.py @@ -0,0 +1,7 @@ +from __future__ import absolute_import, print_function + +from flask import Flask + +raise ImportError() + +testapp = Flask('testapp') diff --git a/tests/test_cli.py b/tests/test_cli.py index 18026a75..313a34d2 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -83,6 +83,7 @@ def test_locate_app(test_apps): pytest.raises(NoAppException, locate_app, "notanpp.py") pytest.raises(NoAppException, locate_app, "cliapp/app") pytest.raises(RuntimeError, locate_app, "cliapp.app:notanapp") + pytest.raises(ImportError, locate_app, "cliapp.importerrorapp") def test_find_default_import_path(test_apps, monkeypatch, tmpdir): From 31e25facd3972b42d118792c847299cc8b2b25a5 Mon Sep 17 00:00:00 2001 From: Paul Brown Date: Fri, 30 Dec 2016 15:40:30 -0600 Subject: [PATCH 082/399] update change log --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index a0a423d1..03194421 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,8 @@ Version 0.12.1 Bugfix release, unreleased +- Prevent `flask run` from showing a NoAppException when an ImportError occurs + within the imported application module. - Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix ``#2118``. From 49386ee69e92aa23edefbfff01fd425c5fbe33d1 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Sat, 31 Dec 2016 16:31:44 +0100 Subject: [PATCH 083/399] Inherit Werkzeug docs (#2135) Fix #2132 --- docs/api.rst | 58 +--------------------------------------------------- 1 file changed, 1 insertion(+), 57 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index d77da3de..7da28dc8 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -30,61 +30,12 @@ Incoming Request Data .. autoclass:: Request :members: - - .. attribute:: form - - A :class:`~werkzeug.datastructures.MultiDict` with the parsed form data from ``POST`` - or ``PUT`` requests. Please keep in mind that file uploads will not - end up here, but instead in the :attr:`files` attribute. - - .. attribute:: args - - A :class:`~werkzeug.datastructures.MultiDict` with the parsed contents of the query - string. (The part in the URL after the question mark). - - .. attribute:: values - - A :class:`~werkzeug.datastructures.CombinedMultiDict` with the contents of both - :attr:`form` and :attr:`args`. - - .. attribute:: cookies - - A :class:`dict` with the contents of all cookies transmitted with - the request. - - .. attribute:: stream - - If the incoming form data was not encoded with a known mimetype - the data is stored unmodified in this stream for consumption. Most - of the time it is a better idea to use :attr:`data` which will give - you that data as a string. The stream only returns the data once. - - .. attribute:: headers - - The incoming request headers as a dictionary like object. - - .. attribute:: data - - Contains the incoming request data as string in case it came with - a mimetype Flask does not handle. - - .. attribute:: files - - A :class:`~werkzeug.datastructures.MultiDict` with files uploaded as part of a - ``POST`` or ``PUT`` request. Each file is stored as - :class:`~werkzeug.datastructures.FileStorage` object. It basically behaves like a - standard file object you know from Python, with the difference that - it also has a :meth:`~werkzeug.datastructures.FileStorage.save` function that can - store the file on the filesystem. + :inherited-members: .. attribute:: environ The underlying WSGI environment. - .. attribute:: method - - The current request method (``POST``, ``GET`` etc.) - .. attribute:: path .. attribute:: full_path .. attribute:: script_root @@ -114,13 +65,6 @@ Incoming Request Data `url_root` ``u'http://www.example.com/myapplication/'`` ============= ====================================================== - .. attribute:: is_xhr - - ``True`` if the request was triggered via a JavaScript - `XMLHttpRequest`. This only works with libraries that support the - ``X-Requested-With`` header and set it to `XMLHttpRequest`. - Libraries that do that are prototype, jQuery and Mochikit and - probably some more. .. class:: request From 92fa444259fe32debf922eddbd6d18e7d69fdfaa Mon Sep 17 00:00:00 2001 From: wgwz Date: Sat, 31 Dec 2016 12:08:25 -0500 Subject: [PATCH 084/399] Moves largerapp into patterns dir and add test - also adds this pattern into tox for testing --- examples/{ => patterns}/largerapp/setup.py | 0 examples/patterns/largerapp/tests/test_largerapp.py | 12 ++++++++++++ .../largerapp/yourapplication/__init__.py | 0 .../largerapp/yourapplication/static/style.css | 0 .../largerapp/yourapplication/templates/index.html | 0 .../largerapp/yourapplication/templates/layout.html | 0 .../largerapp/yourapplication/templates/login.html | 0 .../largerapp/yourapplication/views.py | 0 tox.ini | 1 + 9 files changed, 13 insertions(+) rename examples/{ => patterns}/largerapp/setup.py (100%) create mode 100644 examples/patterns/largerapp/tests/test_largerapp.py rename examples/{ => patterns}/largerapp/yourapplication/__init__.py (100%) rename examples/{ => patterns}/largerapp/yourapplication/static/style.css (100%) rename examples/{ => patterns}/largerapp/yourapplication/templates/index.html (100%) rename examples/{ => patterns}/largerapp/yourapplication/templates/layout.html (100%) rename examples/{ => patterns}/largerapp/yourapplication/templates/login.html (100%) rename examples/{ => patterns}/largerapp/yourapplication/views.py (100%) diff --git a/examples/largerapp/setup.py b/examples/patterns/largerapp/setup.py similarity index 100% rename from examples/largerapp/setup.py rename to examples/patterns/largerapp/setup.py diff --git a/examples/patterns/largerapp/tests/test_largerapp.py b/examples/patterns/largerapp/tests/test_largerapp.py new file mode 100644 index 00000000..acd33c5c --- /dev/null +++ b/examples/patterns/largerapp/tests/test_largerapp.py @@ -0,0 +1,12 @@ +from yourapplication import app +import pytest + +@pytest.fixture +def client(request): + app.config['TESTING'] = True + client = app.test_client() + return client + +def test_index(client): + rv = client.get('/') + assert b"Hello World!" in rv.data \ No newline at end of file diff --git a/examples/largerapp/yourapplication/__init__.py b/examples/patterns/largerapp/yourapplication/__init__.py similarity index 100% rename from examples/largerapp/yourapplication/__init__.py rename to examples/patterns/largerapp/yourapplication/__init__.py diff --git a/examples/largerapp/yourapplication/static/style.css b/examples/patterns/largerapp/yourapplication/static/style.css similarity index 100% rename from examples/largerapp/yourapplication/static/style.css rename to examples/patterns/largerapp/yourapplication/static/style.css diff --git a/examples/largerapp/yourapplication/templates/index.html b/examples/patterns/largerapp/yourapplication/templates/index.html similarity index 100% rename from examples/largerapp/yourapplication/templates/index.html rename to examples/patterns/largerapp/yourapplication/templates/index.html diff --git a/examples/largerapp/yourapplication/templates/layout.html b/examples/patterns/largerapp/yourapplication/templates/layout.html similarity index 100% rename from examples/largerapp/yourapplication/templates/layout.html rename to examples/patterns/largerapp/yourapplication/templates/layout.html diff --git a/examples/largerapp/yourapplication/templates/login.html b/examples/patterns/largerapp/yourapplication/templates/login.html similarity index 100% rename from examples/largerapp/yourapplication/templates/login.html rename to examples/patterns/largerapp/yourapplication/templates/login.html diff --git a/examples/largerapp/yourapplication/views.py b/examples/patterns/largerapp/yourapplication/views.py similarity index 100% rename from examples/largerapp/yourapplication/views.py rename to examples/patterns/largerapp/yourapplication/views.py diff --git a/tox.ini b/tox.ini index 406de5dd..c070a629 100644 --- a/tox.ini +++ b/tox.ini @@ -10,6 +10,7 @@ commands = # We need to install those after Flask is installed. pip install -e examples/flaskr pip install -e examples/minitwit + pip install -e examples/patterns/largerapp py.test --cov=flask --cov-report html [] deps= pytest From 46c1383919454ae281967316d6d6fb33bce9b773 Mon Sep 17 00:00:00 2001 From: wgwz Date: Sat, 31 Dec 2016 12:37:39 -0500 Subject: [PATCH 085/399] Remove unneccessary arg in client fixture --- examples/patterns/largerapp/tests/test_largerapp.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/patterns/largerapp/tests/test_largerapp.py b/examples/patterns/largerapp/tests/test_largerapp.py index acd33c5c..6bc0531e 100644 --- a/examples/patterns/largerapp/tests/test_largerapp.py +++ b/examples/patterns/largerapp/tests/test_largerapp.py @@ -2,7 +2,7 @@ from yourapplication import app import pytest @pytest.fixture -def client(request): +def client(): app.config['TESTING'] = True client = app.test_client() return client From 1b7258f816c2e025acc03a4e775d41a9d4477850 Mon Sep 17 00:00:00 2001 From: wgwz Date: Sat, 31 Dec 2016 18:51:00 -0500 Subject: [PATCH 086/399] Provides a link to the examples src - moved the link towards the top for better visibility --- docs/patterns/packages.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/patterns/packages.rst b/docs/patterns/packages.rst index d1780ca8..1bb84f8c 100644 --- a/docs/patterns/packages.rst +++ b/docs/patterns/packages.rst @@ -17,6 +17,10 @@ this:: login.html ... +If you find yourself stuck on something, feel free +to take a look at the source code for this example. +You'll find `the full src for this example here`_. + Simple Packages --------------- @@ -114,10 +118,6 @@ You should then end up with something like that:: login.html ... -If you find yourself stuck on something, feel free -to take a look at the source code for this example. -You'll find it located under ``flask/examples/largerapp``. - .. admonition:: Circular Imports Every Python programmer hates them, and yet we just added some: @@ -134,6 +134,7 @@ You'll find it located under ``flask/examples/largerapp``. .. _working-with-modules: +.. _the full src for this example here: https://github.com/pallets/flask/tree/master/examples/patterns/largerapp Working with Blueprints ----------------------- From 09973a7387d6239bae310af6c1d580f191b47c0a Mon Sep 17 00:00:00 2001 From: Bryce Guinta Date: Sun, 1 Jan 2017 19:51:21 -0700 Subject: [PATCH 087/399] Fix fastcgi lighttpd example documentation. (#2138) Add a trailing slash to the dummy path in the fastcgi lighttpd setup documentation. Omitting a trailing slash leads to unintended behavior. --- 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 c0beae0c..efae5163 100644 --- a/docs/deploying/fastcgi.rst +++ b/docs/deploying/fastcgi.rst @@ -144,7 +144,7 @@ A basic FastCGI configuration for lighttpd looks like that:: ) alias.url = ( - "/static/" => "/path/to/your/static" + "/static/" => "/path/to/your/static/" ) url.rewrite-once = ( From 88111ae6bf2e33f7e43c44e0cbb32b3d952e4a3a Mon Sep 17 00:00:00 2001 From: Adrian Moennich Date: Tue, 10 Jan 2017 13:12:18 +0100 Subject: [PATCH 088/399] Do not suggest deprecated flask.ext.* --- docs/extensiondev.rst | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index d73d6019..c9a72094 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -29,12 +29,6 @@ be something like "Flask-SimpleXML". Make sure to include the name This is how users can then register dependencies to your extension in their :file:`setup.py` files. -Flask sets up a redirect package called :data:`flask.ext` where users -should import the extensions from. If you for instance have a package -called ``flask_something`` users would import it as -``flask.ext.something``. This is done to transition from the old -namespace packages. See :ref:`ext-import-transition` for more details. - But what do extensions look like themselves? An extension has to ensure that it works with multiple Flask application instances at once. This is a requirement because many people will use patterns like the @@ -393,8 +387,6 @@ extension to be approved you have to follow these guidelines: Python 2.7 -.. _ext-import-transition: - Extension Import Transition --------------------------- From 01b992b1a1482246d705ffe3b3d0dd7816f0456b Mon Sep 17 00:00:00 2001 From: Andrew Arendt Date: Tue, 10 Jan 2017 11:20:53 -0600 Subject: [PATCH 089/399] Added python3.6 support for tests --- .travis.yml | 6 +++++- tests/test_basic.py | 2 +- tests/test_ext.py | 4 ++-- tox.ini | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 0f99a7e8..32247c58 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,6 +8,7 @@ python: - "3.3" - "3.4" - "3.5" + - "3.6" env: - REQUIREMENTS=lowest @@ -32,7 +33,10 @@ matrix: env: REQUIREMENTS=lowest - python: "3.5" env: REQUIREMENTS=lowest-simplejson - + - python: "3.6" + env: REQUIREMENTS=lowest + - python: "3.6" + env: REQUIREMENTS=lowest-simplejson install: - pip install tox diff --git a/tests/test_basic.py b/tests/test_basic.py index be3d5edd..a099c904 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -333,7 +333,7 @@ def test_session_expiration(): client = app.test_client() rv = client.get('/') assert 'set-cookie' in rv.headers - match = re.search(r'\bexpires=([^;]+)(?i)', rv.headers['set-cookie']) + match = re.search(r'(?i)\bexpires=([^;]+)', rv.headers['set-cookie']) expires = parse_date(match.group()) expected = datetime.utcnow() + app.permanent_session_lifetime assert expires.year == expected.year diff --git a/tests/test_ext.py b/tests/test_ext.py index d336e404..ebb5f02d 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -179,8 +179,8 @@ def test_flaskext_broken_package_no_module_caching(flaskext_broken): def test_no_error_swallowing(flaskext_broken): with pytest.raises(ImportError) as excinfo: import flask.ext.broken - - assert excinfo.type is ImportError + # python3.6 raises a subclass of ImportError: 'ModuleNotFoundError' + assert issubclass(excinfo.type, ImportError) if PY2: message = 'No module named missing_module' else: diff --git a/tox.ini b/tox.ini index 406de5dd..1ffdd5da 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = {py26,py27,pypy}-{lowest,release,devel}{,-simplejson}, {py33,py34,py35}-{release,devel}{,-simplejson} +envlist = {py26,py27,pypy}-{lowest,release,devel}{,-simplejson}, {py33,py34,py35,py36}-{release,devel}{,-simplejson} From 9dcfd05d295574488bd0c3644599d1e97dcca3da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Nov=C3=BD?= Date: Fri, 13 Jan 2017 10:54:55 +0100 Subject: [PATCH 090/399] Use SOURCE_DATE_EPOCH for copyright year to make build reproducible Details: https://wiki.debian.org/ReproducibleBuilds/TimestampsProposal --- docs/conf.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index b37427a8..81106a3a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -11,10 +11,13 @@ # All configuration values have a default; values that are commented out # serve to show the default. from __future__ import print_function -from datetime import datetime import os import sys import pkg_resources +import time +import datetime + +BUILD_DATE = datetime.datetime.utcfromtimestamp(int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))) # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the @@ -49,7 +52,7 @@ master_doc = 'index' # General information about the project. project = u'Flask' -copyright = u'2010 - {0}, Armin Ronacher'.format(datetime.utcnow().year) +copyright = u'2010 - {0}, Armin Ronacher'.format(BUILD_DATE.year) # 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 9900a72fe7992d873915af68f7d52148e738d032 Mon Sep 17 00:00:00 2001 From: Dennis Chen Date: Sat, 14 Jan 2017 12:58:45 -0800 Subject: [PATCH 091/399] Fix Request Reference (#2151) Points flask.Request to appropriate place in the documentation. --- docs/quickstart.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index b444e080..749a1f75 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -538,16 +538,16 @@ The Request Object `````````````````` The request object is documented in the API section and we will not cover -it here in detail (see :class:`~flask.request`). Here is a broad overview of +it here in detail (see :class:`~flask.Request`). Here is a broad overview of some of the most common operations. First of all you have to import it from the ``flask`` module:: from flask import request The current request method is available by using the -:attr:`~flask.request.method` attribute. To access form data (data +:attr:`~flask.Request.method` attribute. To access form data (data transmitted in a ``POST`` or ``PUT`` request) you can use the -:attr:`~flask.request.form` attribute. Here is a full example of the two +:attr:`~flask.Request.form` attribute. Here is a full example of the two attributes mentioned above:: @app.route('/login', methods=['POST', 'GET']) @@ -570,7 +570,7 @@ error page is shown instead. So for many situations you don't have to deal with that problem. To access parameters submitted in the URL (``?key=value``) you can use the -:attr:`~flask.request.args` attribute:: +:attr:`~flask.Request.args` attribute:: searchword = request.args.get('key', '') @@ -579,7 +579,7 @@ We recommend accessing URL parameters with `get` or by catching the bad request page in that case is not user friendly. For a full list of methods and attributes of the request object, head over -to the :class:`~flask.request` documentation. +to the :class:`~flask.Request` documentation. File Uploads From 3fc8be5a4e9c1db58dcc74d3d48bf70b6a8db932 Mon Sep 17 00:00:00 2001 From: Kim Blomqvist Date: Tue, 17 Jan 2017 17:15:51 +0200 Subject: [PATCH 092/399] Disable debug when FLASK_DEBUG=False (#2155) Convert FLASK_DEBUG envvar to lower before test if in tuple --- flask/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/helpers.py b/flask/helpers.py index c6c2cddc..2f446327 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -58,7 +58,7 @@ def get_debug_flag(default=None): val = os.environ.get('FLASK_DEBUG') if not val: return default - return val not in ('0', 'false', 'no') + return val.lower() not in ('0', 'false', 'no') def _endpoint_from_view_func(view_func): From fe7910ccd59242af106fcc67cfd47e6e2355787d Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Tue, 17 Jan 2017 11:20:07 -0800 Subject: [PATCH 093/399] Update docs that request is an object, not a class (#2154) Cleanup sphinx formatting to show that `request` is an object, not a class. The actual class name is `Request`. Based on discussion [here](https://github.com/pallets/flask/pull/2151#issuecomment-272699147). --- docs/api.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/api.rst b/docs/api.rst index 7da28dc8..b5009907 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -66,7 +66,7 @@ Incoming Request Data ============= ====================================================== -.. class:: request +.. attribute:: request To access incoming request data, you can use the global `request` object. Flask parses incoming request data for you and gives you From 1636a4c410a1bf3713bc1da8d4de7ca66cdf1681 Mon Sep 17 00:00:00 2001 From: Raphael Deem Date: Tue, 17 Jan 2017 13:22:16 -0800 Subject: [PATCH 094/399] use SERVER_NAME to set host and port in app.run() (#2152) --- flask/app.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/flask/app.py b/flask/app.py index 942992dc..92ee8772 100644 --- a/flask/app.py +++ b/flask/app.py @@ -825,14 +825,14 @@ class Flask(_PackageBoundObject): information. """ from werkzeug.serving import run_simple - if host is None: - host = '127.0.0.1' - if port is None: - server_name = self.config['SERVER_NAME'] - if server_name and ':' in server_name: - port = int(server_name.rsplit(':', 1)[1]) - else: - port = 5000 + _host = '127.0.0.1' + _port = 5000 + server_name = self.config.get("SERVER_NAME") + sn_host, sn_port = None, None + if server_name: + sn_host, _, sn_port = server_name.partition(':') + host = host or sn_host or _host + port = int(port or sn_port or _port) if debug is not None: self.debug = bool(debug) options.setdefault('use_reloader', self.debug) From 42fbbb4cbbd312464214dd66d0985828c16dce67 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 17 Jan 2017 14:08:33 -0800 Subject: [PATCH 095/399] add test and changelog for SERVER_NAME app.run default ref #2152 --- CHANGES | 2 ++ tests/test_basic.py | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/CHANGES b/CHANGES index b096b0fe..157e92e3 100644 --- a/CHANGES +++ b/CHANGES @@ -17,6 +17,8 @@ Bugfix release, unreleased within the imported application module. - Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix ``#2118``. +- Use the``SERVER_NAME`` config if it is present as default values for + ``app.run``. ``#2109``, ``#2152`` Version 0.12 ------------ diff --git a/tests/test_basic.py b/tests/test_basic.py index a099c904..6341234b 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1681,3 +1681,20 @@ def test_run_server_port(monkeypatch): hostname, port = 'localhost', 8000 app.run(hostname, port, debug=True) assert rv['result'] == 'running on %s:%s ...' % (hostname, port) + + +@pytest.mark.parametrize('host,port,expect_host,expect_port', ( + (None, None, 'pocoo.org', 8080), + ('localhost', None, 'localhost', 8080), + (None, 80, 'pocoo.org', 80), + ('localhost', 80, 'localhost', 80), +)) +def test_run_from_config(monkeypatch, host, port, expect_host, expect_port): + def run_simple_mock(hostname, port, *args, **kwargs): + assert hostname == expect_host + assert port == expect_port + + monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) + app = flask.Flask(__name__) + app.config['SERVER_NAME'] = 'pocoo.org:8080' + app.run(host, port) From c9b33d0e860e347f1ed46eebadbfef4f5422b6da Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sun, 29 Jan 2017 12:26:52 +0100 Subject: [PATCH 096/399] Convert Flask.run into a noop when run from the CLI --- CHANGES | 4 ++++ flask/app.py | 7 +++++++ flask/cli.py | 7 +++++++ flask/debughelpers.py | 12 ++++++++++++ 4 files changed, 30 insertions(+) diff --git a/CHANGES b/CHANGES index 157e92e3..62bb2004 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,10 @@ Version 0.13 Major release, unreleased +- Make `app.run()` into a noop if a Flask application is run from the + development server on the command line. This avoids some behavior that + was confusing to debug for newcomers. + Version 0.12.1 -------------- diff --git a/flask/app.py b/flask/app.py index 92ee8772..27918d01 100644 --- a/flask/app.py +++ b/flask/app.py @@ -824,6 +824,13 @@ class Flask(_PackageBoundObject): :func:`werkzeug.serving.run_simple` for more information. """ + # Change this into a no-op if the server is invoked from the + # command line. Have a look at cli.py for more information. + if os.environ.get('FLASK_RUN_FROM_CLI_SERVER') == '1': + from .debughelpers import explain_ignored_app_run + explain_ignored_app_run() + return + from werkzeug.serving import run_simple _host = '127.0.0.1' _port = 5000 diff --git a/flask/cli.py b/flask/cli.py index 074ee768..bde5a13b 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -412,6 +412,13 @@ def run_command(info, host, port, reload, debugger, eager_loading, """ from werkzeug.serving import run_simple + # Set a global flag that indicates that we were invoked from the + # command line interface provided server command. This is detected + # by Flask.run to make the call into a no-op. This is necessary to + # avoid ugly errors when the script that is loaded here also attempts + # to start a server. + os.environ['FLASK_RUN_FROM_CLI_SERVER'] = '1' + debug = get_debug_flag() if reload is None: reload = bool(debug) diff --git a/flask/debughelpers.py b/flask/debughelpers.py index 90710dd3..9e44fe69 100644 --- a/flask/debughelpers.py +++ b/flask/debughelpers.py @@ -8,6 +8,9 @@ :copyright: (c) 2015 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ +import os +from warnings import warn + from ._compat import implements_to_string, text_type from .app import Flask from .blueprints import Blueprint @@ -153,3 +156,12 @@ def explain_template_loading_attempts(app, template, attempts): info.append(' See http://flask.pocoo.org/docs/blueprints/#templates') app.logger.info('\n'.join(info)) + + +def explain_ignored_app_run(): + if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': + warn(Warning('Silently ignoring app.run() because the ' + 'application is run from the flask command line ' + 'executable. Consider putting app.run() behind an ' + 'if __name__ == "__main__" guard to silence this ' + 'warning.'), stacklevel=3) From f84fdadda9f675a3e94a042790e010be07927af4 Mon Sep 17 00:00:00 2001 From: Swan Htet Aung Date: Thu, 9 Feb 2017 18:01:12 +0630 Subject: [PATCH 097/399] Update 4.4.3 HTTP Methods Example Otherwise it produces `ValueError: View function did not return a response`. --- docs/quickstart.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 749a1f75..b619185d 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -306,9 +306,9 @@ can be changed by providing the ``methods`` argument to the @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': - do_the_login() + return do_the_login() else: - show_the_login_form() + return show_the_login_form() If ``GET`` is present, ``HEAD`` will be added automatically for you. You don't have to deal with that. It will also make sure that ``HEAD`` requests From 95db82f8f7df0acce7051a8dedf29b88e436f5ab Mon Sep 17 00:00:00 2001 From: vojtekb Date: Thu, 9 Feb 2017 18:34:16 +0100 Subject: [PATCH 098/399] py.test => pytest (#2173) py.test => pytest --- CONTRIBUTING.rst | 10 +++++----- README | 4 ++-- docs/tutorial/testing.rst | 2 +- setup.cfg | 2 +- tox.ini | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index d9cd2214..1c9c8912 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -38,7 +38,7 @@ Running the testsuite You probably want to set up a `virtualenv `_. -The minimal requirement for running the testsuite is ``py.test``. You can +The minimal requirement for running the testsuite is ``pytest``. You can install it with:: pip install pytest @@ -54,9 +54,9 @@ Install Flask as an editable package using the current source:: Then you can run the testsuite with:: - py.test + pytest -With only py.test installed, a large part of the testsuite will get skipped +With only pytest installed, a large part of the testsuite will get skipped though. Whether this is relevant depends on which part of Flask you're working on. Travis is set up to run the full testsuite when you submit your pull request anyways. @@ -79,11 +79,11 @@ plugin. This assumes you have already run the testsuite (see previous section): After this has been installed, you can output a report to the command line using this command:: - py.test --cov=flask tests/ + pytest --cov=flask tests/ Generate a HTML report can be done using this command:: - py.test --cov-report html --cov=flask tests/ + pytest --cov-report html --cov=flask tests/ Full docs on ``coverage.py`` are here: https://coverage.readthedocs.io diff --git a/README b/README index baea6b24..75c5e7b1 100644 --- a/README +++ b/README @@ -33,9 +33,9 @@ Good that you're asking. The tests are in the tests/ folder. To run the tests use the - `py.test` testing tool: + `pytest` testing tool: - $ py.test + $ pytest Details on contributing can be found in CONTRIBUTING.rst diff --git a/docs/tutorial/testing.rst b/docs/tutorial/testing.rst index dcf36594..26099375 100644 --- a/docs/tutorial/testing.rst +++ b/docs/tutorial/testing.rst @@ -46,7 +46,7 @@ At this point you can run the tests. Here ``pytest`` will be used. Run and watch the tests pass, within the top-level :file:`flaskr/` directory as:: - py.test + pytest Testing + setuptools -------------------- diff --git a/setup.cfg b/setup.cfg index cd282e48..0e97b0ba 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,4 +8,4 @@ universal = 1 license_file = LICENSE [tool:pytest] -norecursedirs = .* *.egg *.egg-info env* artwork docs +norecursedirs = .* *.egg *.egg-info env* artwork docs examples diff --git a/tox.ini b/tox.ini index 8d5a0f43..764b4030 100644 --- a/tox.ini +++ b/tox.ini @@ -11,7 +11,7 @@ commands = pip install -e examples/flaskr pip install -e examples/minitwit pip install -e examples/patterns/largerapp - py.test --cov=flask --cov-report html [] + pytest --cov=flask --cov-report html [] deps= pytest pytest-cov From 89798ea7dd549ba8e06112e3b44b0cb7d9d4f417 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Thu, 9 Feb 2017 18:35:21 +0100 Subject: [PATCH 099/399] Remove examples dir again --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 0e97b0ba..cd282e48 100644 --- a/setup.cfg +++ b/setup.cfg @@ -8,4 +8,4 @@ universal = 1 license_file = LICENSE [tool:pytest] -norecursedirs = .* *.egg *.egg-info env* artwork docs examples +norecursedirs = .* *.egg *.egg-info env* artwork docs From 5efb1632371048dcb5cd622b6d597c6b69b901b5 Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Fri, 10 Feb 2017 03:19:59 -0800 Subject: [PATCH 100/399] bdist_wheel replaces wheel (#2179) https://packaging.python.org/distributing/#universal-wheels --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index cd282e48..781de592 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,7 +1,7 @@ [aliases] release = egg_info -RDb '' -[wheel] +[bdist_wheel] universal = 1 [metadata] From bb0e755c808a8541192982ba7b86308b68ff7657 Mon Sep 17 00:00:00 2001 From: Jeff Widman Date: Sat, 11 Feb 2017 01:43:11 -0800 Subject: [PATCH 101/399] Migrate various docs links to https (#2180) Also fixed a few outdated links --- CONTRIBUTING.rst | 2 +- docs/_templates/sidebarintro.html | 6 +++--- docs/conf.py | 2 +- docs/deploying/fastcgi.rst | 6 +++--- docs/deploying/index.rst | 2 +- docs/deploying/mod_wsgi.rst | 8 ++++---- docs/deploying/uwsgi.rst | 4 ++-- docs/errorhandling.rst | 4 ++-- docs/extensiondev.rst | 2 +- docs/installation.rst | 2 +- docs/patterns/appfactories.rst | 2 +- docs/patterns/favicon.rst | 2 +- docs/patterns/fileuploads.rst | 2 +- docs/patterns/sqlalchemy.rst | 6 +++--- docs/patterns/sqlite3.rst | 14 +++++++------- docs/patterns/wtforms.rst | 2 +- docs/quickstart.rst | 8 ++++---- docs/security.rst | 2 +- docs/tutorial/introduction.rst | 2 +- docs/upgrading.rst | 2 +- examples/minitwit/minitwit/minitwit.py | 2 +- setup.py | 4 ++-- 22 files changed, 43 insertions(+), 43 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 1c9c8912..66766512 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -28,7 +28,7 @@ Submitting patches clearly under which circumstances the bug happens. Make sure the test fails without your patch. -- Try to follow `PEP8 `_, but you +- Try to follow `PEP8 `_, but you may ignore the line-length-limit if following it would make the code uglier. diff --git a/docs/_templates/sidebarintro.html b/docs/_templates/sidebarintro.html index ec1608fd..71fcd73b 100644 --- a/docs/_templates/sidebarintro.html +++ b/docs/_templates/sidebarintro.html @@ -16,7 +16,7 @@

Useful Links

diff --git a/docs/conf.py b/docs/conf.py index 81106a3a..8682dd8c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -260,7 +260,7 @@ intersphinx_mapping = { 'werkzeug': ('http://werkzeug.pocoo.org/docs/', None), 'click': ('http://click.pocoo.org/', None), 'jinja': ('http://jinja.pocoo.org/docs/', None), - 'sqlalchemy': ('http://docs.sqlalchemy.org/en/latest/', None), + 'sqlalchemy': ('https://docs.sqlalchemy.org/en/latest/', None), 'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None), 'blinker': ('https://pythonhosted.org/blinker/', None) } diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst index efae5163..5ca2a084 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 @@ -234,7 +234,7 @@ python path. Common problems are: web server. - Different python interpreters being used. -.. _nginx: http://nginx.org/ -.. _lighttpd: http://www.lighttpd.net/ +.. _nginx: https://nginx.org/ +.. _lighttpd: https://www.lighttpd.net/ .. _cherokee: http://cherokee-project.com/ .. _flup: https://pypi.python.org/pypi/flup diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst index 20e71762..6950e47a 100644 --- a/docs/deploying/index.rst +++ b/docs/deploying/index.rst @@ -21,7 +21,7 @@ Hosted options - `Deploying Flask on OpenShift `_ - `Deploying Flask on Webfaction `_ - `Deploying Flask on Google App Engine `_ -- `Deploying Flask on AWS Elastic Beanstalk `_ +- `Deploying Flask on AWS Elastic Beanstalk `_ - `Sharing your Localhost Server with Localtunnel `_ - `Deploying on Azure (IIS) `_ - `Deploying on PythonAnywhere `_ diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index 0f4af6c3..ca694b7d 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -13,7 +13,7 @@ If you are using the `Apache`_ webserver, consider using `mod_wsgi`_. not called because this will always start a local WSGI server which we do not want if we deploy that application to mod_wsgi. -.. _Apache: http://httpd.apache.org/ +.. _Apache: https://httpd.apache.org/ Installing `mod_wsgi` --------------------- @@ -114,7 +114,7 @@ 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 +.. _Apache 2.4: https://httpd.apache.org/docs/trunk/upgrading.html Most notably, the syntax for directory permissions has changed from httpd 2.2 @@ -133,9 +133,9 @@ to httpd 2.4 syntax For more information consult the `mod_wsgi documentation`_. .. _mod_wsgi: https://github.com/GrahamDumpleton/mod_wsgi -.. _installation instructions: http://modwsgi.readthedocs.io/en/develop/installation.html +.. _installation instructions: https://modwsgi.readthedocs.io/en/develop/installation.html .. _virtual python: https://pypi.python.org/pypi/virtualenv -.. _mod_wsgi documentation: http://modwsgi.readthedocs.io/en/develop/index.html +.. _mod_wsgi documentation: https://modwsgi.readthedocs.io/en/develop/index.html Troubleshooting --------------- diff --git a/docs/deploying/uwsgi.rst b/docs/deploying/uwsgi.rst index fc991e72..50c85fb2 100644 --- a/docs/deploying/uwsgi.rst +++ b/docs/deploying/uwsgi.rst @@ -66,7 +66,7 @@ to have it in the URL root its a bit simpler:: uwsgi_pass unix:/tmp/yourapplication.sock; } -.. _nginx: http://nginx.org/ -.. _lighttpd: http://www.lighttpd.net/ +.. _nginx: https://nginx.org/ +.. _lighttpd: https://www.lighttpd.net/ .. _cherokee: http://cherokee-project.com/ .. _uwsgi: http://projects.unbit.it/uwsgi/ diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst index 64c0f8b3..2791fec3 100644 --- a/docs/errorhandling.rst +++ b/docs/errorhandling.rst @@ -34,7 +34,7 @@ Error Logging Tools Sending error mails, even if just for critical ones, can become overwhelming if enough users are hitting the error and log files are typically never looked at. This is why we recommend using `Sentry -`_ for dealing with application errors. It's +`_ for dealing with application errors. It's available as an Open Source project `on GitHub `__ and is also available as a `hosted version `_ which you can try for free. Sentry @@ -89,7 +89,7 @@ Register error handlers using :meth:`~flask.Flask.errorhandler` or @app.errorhandler(werkzeug.exceptions.BadRequest) def handle_bad_request(e): return 'bad request!' - + app.register_error_handler(400, lambda e: 'bad request!') Those two ways are equivalent, but the first one is more clear and leaves diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index c9a72094..9ae6e6f1 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -405,6 +405,6 @@ schema. The ``flask.ext.foo`` compatibility alias is still in Flask 0.11 but is now deprecated -- you should use ``flask_foo``. -.. _OAuth extension: http://pythonhosted.org/Flask-OAuth/ +.. _OAuth extension: https://pythonhosted.org/Flask-OAuth/ .. _mailinglist: http://flask.pocoo.org/mailinglist/ .. _IRC channel: http://flask.pocoo.org/community/irc/ diff --git a/docs/installation.rst b/docs/installation.rst index 6f833eac..96c363f5 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -112,7 +112,7 @@ it to operate on a git checkout. Either way, virtualenv is recommended. Get the git checkout in a new virtualenv and run in development mode:: - $ git clone http://github.com/pallets/flask.git + $ git clone https://github.com/pallets/flask.git Initialized empty Git repository in ~/dev/flask/.git/ $ cd flask $ virtualenv venv diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index c118a273..fdbde504 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -60,7 +60,7 @@ Factories & Extensions 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 `_, +Using `Flask-SQLAlchemy `_, as an example, you should not do something along those lines:: def create_app(config_filename): diff --git a/docs/patterns/favicon.rst b/docs/patterns/favicon.rst index acdee24b..21ea767f 100644 --- a/docs/patterns/favicon.rst +++ b/docs/patterns/favicon.rst @@ -49,5 +49,5 @@ web server's documentation. See also -------- -* The `Favicon `_ article on +* The `Favicon `_ article on Wikipedia diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index 8ab8c033..dc3820be 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -181,4 +181,4 @@ applications dealing with uploads, there is also a Flask extension called blacklisting of extensions and more. .. _jQuery: https://jquery.com/ -.. _Flask-Uploads: http://pythonhosted.org/Flask-Uploads/ +.. _Flask-Uploads: https://pythonhosted.org/Flask-Uploads/ diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index 9c985cc6..8785a6e2 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -108,9 +108,9 @@ Querying is simple as well: >>> User.query.filter(User.name == 'admin').first() -.. _SQLAlchemy: http://www.sqlalchemy.org/ +.. _SQLAlchemy: https://www.sqlalchemy.org/ .. _declarative: - http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/ + https://docs.sqlalchemy.org/en/latest/orm/extensions/declarative/ Manual Object Relational Mapping -------------------------------- @@ -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/sqlite3.rst b/docs/patterns/sqlite3.rst index 66a7c4c4..15f38ea7 100644 --- a/docs/patterns/sqlite3.rst +++ b/docs/patterns/sqlite3.rst @@ -3,8 +3,8 @@ Using SQLite 3 with Flask ========================= -In Flask you can easily implement the opening of database connections on -demand and closing them when the context dies (usually at the end of the +In Flask you can easily implement the opening of database connections on +demand and closing them when the context dies (usually at the end of the request). Here is a simple example of how you can use SQLite 3 with Flask:: @@ -71,7 +71,7 @@ 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 could be inserted into the ``get_db`` +dictionaries instead of tuples, this could be inserted into the ``get_db`` function we created above:: def make_dicts(cursor, row): @@ -102,15 +102,15 @@ This would use Row objects rather than dicts to return the results of queries. T Additionally, it is a good idea to provide a query function that combines getting the cursor, executing and fetching the results:: - + def query_db(query, args=(), one=False): cur = get_db().execute(query, args) rv = cur.fetchall() cur.close() return (rv[0] if rv else None) if one else rv -This handy little function, in combination with a row factory, makes -working with the database much more pleasant than it is by just using the +This handy little function, in combination with a row factory, makes +working with the database much more pleasant than it is by just using the raw cursor and connection objects. Here is how you can use it:: @@ -131,7 +131,7 @@ To pass variable parts to the SQL statement, use a question mark in the statement and pass in the arguments as a list. Never directly add them to the SQL statement with string formatting because this makes it possible to attack the application using `SQL Injections -`_. +`_. Initial Schemas --------------- diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst index 2649cad6..0e53de17 100644 --- a/docs/patterns/wtforms.rst +++ b/docs/patterns/wtforms.rst @@ -19,7 +19,7 @@ forms. fun. You can get it from `PyPI `_. -.. _Flask-WTF: http://pythonhosted.org/Flask-WTF/ +.. _Flask-WTF: https://flask-wtf.readthedocs.io/en/stable/ The Forms --------- diff --git a/docs/quickstart.rst b/docs/quickstart.rst index b619185d..7ce8a90f 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -102,9 +102,9 @@ docs to see the alternative method for running a server. Invalid Import Name ``````````````````` -The ``FLASK_APP`` environment variable is the name of the module to import at -:command:`flask run`. 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 +The ``FLASK_APP`` environment variable is the name of the module to import at +:command:`flask run`. 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 @@ -367,7 +367,7 @@ HTTP has become quite popular lately and browsers are no longer the only clients that are using HTTP. For instance, many revision control systems use it. -.. _HTTP RFC: http://www.ietf.org/rfc/rfc2068.txt +.. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt Static Files ------------ diff --git a/docs/security.rst b/docs/security.rst index 587bd4ef..ad0d1244 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -15,7 +15,7 @@ it JavaScript) into the context of a website. To remedy this, developers have to properly escape text so that it cannot include arbitrary HTML tags. For more information on that have a look at the Wikipedia article on `Cross-Site Scripting -`_. +`_. Flask configures Jinja2 to automatically escape all values unless explicitly told otherwise. This should rule out all XSS problems caused diff --git a/docs/tutorial/introduction.rst b/docs/tutorial/introduction.rst index dd46628b..1abe597f 100644 --- a/docs/tutorial/introduction.rst +++ b/docs/tutorial/introduction.rst @@ -31,4 +31,4 @@ Here a screenshot of the final application: Continue with :ref:`tutorial-folders`. -.. _SQLAlchemy: http://www.sqlalchemy.org/ +.. _SQLAlchemy: https://www.sqlalchemy.org/ diff --git a/docs/upgrading.rst b/docs/upgrading.rst index 41b70f03..436b0430 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -143,7 +143,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://pythonhosted.org/Flask-OldSessions/ +.. _Flask-OldSessions: https://pythonhosted.org/Flask-OldSessions/ Version 0.9 ----------- diff --git a/examples/minitwit/minitwit/minitwit.py b/examples/minitwit/minitwit/minitwit.py index bbc3b483..69840267 100644 --- a/examples/minitwit/minitwit/minitwit.py +++ b/examples/minitwit/minitwit/minitwit.py @@ -85,7 +85,7 @@ def format_datetime(timestamp): def gravatar_url(email, size=80): """Return the gravatar image for the given email address.""" - return 'http://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \ + return 'https://www.gravatar.com/avatar/%s?d=identicon&s=%d' % \ (md5(email.strip().lower().encode('utf-8')).hexdigest(), size) diff --git a/setup.py b/setup.py index 983f7611..08995073 100644 --- a/setup.py +++ b/setup.py @@ -41,7 +41,7 @@ Links * `website `_ * `documentation `_ * `development version - `_ + `_ """ import re @@ -59,7 +59,7 @@ with open('flask/__init__.py', 'rb') as f: setup( name='Flask', version=version, - url='http://github.com/pallets/flask/', + url='https://github.com/pallets/flask/', license='BSD', author='Armin Ronacher', author_email='armin.ronacher@active-4.com', From eaba4a73aa1013db908ea07af6028056c4fde706 Mon Sep 17 00:00:00 2001 From: Nick Ficano Date: Wed, 15 Feb 2017 11:55:56 -0500 Subject: [PATCH 102/399] Fix typo in file header (jsonimpl => json) --- flask/json.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/json.py b/flask/json.py index 16e0c295..19b337c3 100644 --- a/flask/json.py +++ b/flask/json.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ - flask.jsonimpl - ~~~~~~~~~~~~~~ + flask.json + ~~~~~~~~~~ Implementation helpers for the JSON support in Flask. From dc5f48f587a93d1f921d9d7e06fa05fdea6151d9 Mon Sep 17 00:00:00 2001 From: Timothy John Perisho Eccleston Date: Sat, 18 Feb 2017 00:41:58 -0600 Subject: [PATCH 103/399] Fix typo in docs/tutorial/templates.rst (#2186) --- docs/tutorial/templates.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/templates.rst b/docs/tutorial/templates.rst index 269e8df1..4cb7db7f 100644 --- a/docs/tutorial/templates.rst +++ b/docs/tutorial/templates.rst @@ -59,7 +59,7 @@ show_entries.html This template extends the :file:`layout.html` template from above to display the messages. Note that the ``for`` loop iterates over the messages we passed in with the :func:`~flask.render_template` function. Notice that the form is -configured to to submit to the `add_entry` view function and use ``POST`` as +configured to submit to the `add_entry` view function and use ``POST`` as HTTP method: .. sourcecode:: html+jinja From 8a8a60815294eb9d5db0c2d451c6c9d959c7c499 Mon Sep 17 00:00:00 2001 From: Josh Rowe Date: Thu, 23 Feb 2017 15:23:44 +0000 Subject: [PATCH 104/399] Move object_hook outside loads method so class can be extend and reused --- flask/sessions.py | 147 +++++++++++++++++++++++++++++++------------- tests/test_basic.py | 6 ++ 2 files changed, 109 insertions(+), 44 deletions(-) diff --git a/flask/sessions.py b/flask/sessions.py index 525ff246..4335eeaa 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -8,7 +8,6 @@ :copyright: (c) 2015 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ - import uuid import hashlib from base64 import b64encode, b64decode @@ -49,22 +48,82 @@ 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): +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 __init__(self): + self.conversions = [ + { + 'check': lambda value: self._is_dict_with_used_key(value), + 'tag': lambda value: self._tag_dict_used_with_key(value), + 'untag': lambda value: self._untag_dict_used_with_key(value), + 'key': ' di', + }, + { + 'check': lambda value: isinstance(value, tuple), + 'tag': lambda value: [self._tag(x) for x in value], + 'untag': lambda value: tuple(value), + 'key': ' t', + }, + { + 'check': lambda value: isinstance(value, uuid.UUID), + 'tag': lambda value: value.hex, + 'untag': lambda value: uuid.UUID(value), + 'key': ' u', + }, + { + 'check': lambda value: isinstance(value, bytes), + 'tag': lambda value: b64encode(value).decode('ascii'), + 'untag': lambda value: b64decode(value), + 'key': ' b', + }, + { + 'check': lambda value: callable(getattr(value, '__html__', + None)), + 'tag': lambda value: text_type(value.__html__()), + 'untag': lambda value: Markup(value), + 'key': ' m', + }, + { + 'check': lambda value: isinstance(value, list), + 'tag': lambda value: [self._tag(x) for x in value], + }, + { + 'check': lambda value: isinstance(value, datetime), + 'tag': lambda value: http_date(value), + 'untag': lambda value: parse_date(value), + 'key': ' d', + }, + { + 'check': lambda value: isinstance(value, dict), + 'tag': lambda value: dict((k, self._tag(v)) for k, v in + iteritems(value)), + }, + { + 'check': lambda value: isinstance(value, str), + 'tag': lambda value: self._tag_string(value), + } + ] + + @property + def keys(self): + return [c['key'] for c in self.conversions if c.get('key')] + + def _get_conversion_untag(self, key): + return next( + (c['untag'] for c in self.conversions if c.get('key') == key), + lambda v: None + ) + + def _is_dict_with_used_key(self, v): + return isinstance(v, dict) and len(v) == 1 and list(v)[0] in self.keys + + def _was_dict_with_used_key(self, k): + return k.endswith('__') and k[:-2] in self.keys + + def _tag_string(self, value): try: return text_type(value) except UnicodeError: @@ -73,38 +132,38 @@ def _tag(value): 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 _tag_dict_used_with_key(self, value): + k, v = next(iteritems(value)) + return {'%s__' % k: v} + + def _tag(self, value): + for tag_ops in self.conversions: + if tag_ops['check'](value): + tag = tag_ops.get('key') + if tag: + return {tag: tag_ops['tag'](value)} + return tag_ops['tag'](value) + return value + + def _untag_dict_used_with_key(self, the_value): + k, v = next(iteritems(the_value)) + if self._was_dict_with_used_key(k): + return {k[:-2]: self._untag(v)} + + def _untag(self, obj): + if len(obj) != 1: + return obj + the_key, the_value = next(iteritems(obj)) + untag = self._get_conversion_untag(the_key) + new_value = untag(the_value) + return new_value if new_value else obj def dumps(self, value): - return json.dumps(_tag(value), separators=(',', ':')) - - LOADS_MAP = { - ' t': tuple, - ' u': uuid.UUID, - ' b': b64decode, - ' m': Markup, - ' d': parse_date, - } + return json.dumps(self._tag(value), separators=(',', ':')) def loads(self, value): - def object_hook(obj): - if len(obj) != 1: - return obj - the_key, the_value = next(iteritems(obj)) - # Check the key for a corresponding function - return_function = self.LOADS_MAP.get(the_key) - if return_function: - # Pass the value to the function - return return_function(the_value) - # Didn't find a function for this object - return obj - return json.loads(value, object_hook=object_hook) + return json.loads(value, object_hook=self._untag) session_json_serializer = TaggedJSONSerializer() diff --git a/tests/test_basic.py b/tests/test_basic.py index 6341234b..62a90cf6 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -383,6 +383,9 @@ def test_session_special_types(): flask.session['dt'] = now flask.session['b'] = b'\xff' flask.session['t'] = (1, 2, 3) + flask.session['spacefirst'] = {' t': 'not-a-tuple'} + flask.session['withunderscores'] = {' t__': 'not-a-tuple'} + flask.session['notadict'] = {' di': 'not-a-dict'} return response @app.route('/') @@ -399,6 +402,9 @@ def test_session_special_types(): assert rv['b'] == b'\xff' assert type(rv['b']) == bytes assert rv['t'] == (1, 2, 3) + assert rv['spacefirst'] == {' t': 'not-a-tuple'} + assert rv['withunderscores'] == {' t__': 'not-a-tuple'} + assert rv['notadict'] == {' di': 'not-a-dict'} def test_session_cookie_setting(): From af11098057b022c172c1142f02e50cc3c4b065b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sergio=20Di=CC=81az=20Sa=CC=81nchez?= Date: Tue, 28 Feb 2017 00:13:45 +0100 Subject: [PATCH 105/399] Updated documentation for being able to use init_db method --- docs/testing.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 0737936e..f4ee64ec 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -33,7 +33,7 @@ In order to test the application, we add a second module (:file:`flaskr_tests.py`) and create a unittest skeleton there:: import os - import flaskr + from flaskr import flaskr import unittest import tempfile @@ -208,7 +208,7 @@ temporarily. With this you can access the :class:`~flask.request`, functions. Here is a full example that demonstrates this approach:: import flask - + app = flask.Flask(__name__) with app.test_request_context('/?name=Peter'): From fca5577a0097e876e8f8e8d2f3961a40fa44bbeb Mon Sep 17 00:00:00 2001 From: Sebastian Kalinowski Date: Tue, 28 Feb 2017 06:05:09 +0100 Subject: [PATCH 106/399] Remove extra HTML tag from fileupload docs (#2141) --- docs/patterns/fileuploads.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index dc3820be..8bf2287c 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -72,8 +72,8 @@ the file and redirects the user to the URL for the uploaded file:: Upload new File

Upload new File

-

- + +

''' From c43560777a3efeaeaf0eb47568171f04103dc363 Mon Sep 17 00:00:00 2001 From: Grey Li Date: Sat, 4 Mar 2017 18:29:04 +0800 Subject: [PATCH 107/399] Add tips for debug config with flask cli (#2196) * Add tips for debug config with flask cli `app.debug` and `app.config['DEBUG']` are not compatible with the `flask` script. * Grammar fix * Grammar fix --- docs/config.rst | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 6d37c1e8..c36cc852 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -44,6 +44,21 @@ method:: SECRET_KEY='...' ) +.. admonition:: Debug Mode with the ``flask`` Script + + If you use the :command:`flask` script to start a local development + server, to enable the debug mode, you need to export the ``FLASK_DEBUG`` + environment variable before running the server:: + + $ export FLASK_DEBUG=1 + $ flask run + + (On Windows you need to use ``set`` instead of ``export``). + + ``app.debug`` and ``app.config['DEBUG']`` are not compatible with +   the :command:`flask` script. They only worked when using ``Flask.run()`` + method. + Builtin Configuration Values ---------------------------- @@ -52,7 +67,8 @@ The following configuration values are used internally by Flask: .. tabularcolumns:: |p{6.5cm}|p{8.5cm}| ================================= ========================================= -``DEBUG`` enable/disable debug mode +``DEBUG`` enable/disable debug mode when using + ``Flask.run()`` method to start server ``TESTING`` enable/disable testing mode ``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the propagation of exceptions. If not set or From d9a28434af3c946c79d062d7474ace42ef85d798 Mon Sep 17 00:00:00 2001 From: Adrian Date: Sat, 4 Mar 2017 22:32:23 +0100 Subject: [PATCH 108/399] Fix typo --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 62bb2004..2c0a4869 100644 --- a/CHANGES +++ b/CHANGES @@ -21,7 +21,7 @@ Bugfix release, unreleased within the imported application module. - Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix ``#2118``. -- Use the``SERVER_NAME`` config if it is present as default values for +- Use the ``SERVER_NAME`` config if it is present as default values for ``app.run``. ``#2109``, ``#2152`` Version 0.12 From 06112a555a9398701d3269253355c214791e1eca Mon Sep 17 00:00:00 2001 From: Elton Law Date: Sun, 5 Mar 2017 07:07:49 -0500 Subject: [PATCH 109/399] Close
  • tag in tutorial (#2199) Change was merged in the example code but wasn't changed in the docs. https://github.com/pallets/flask/commit/c54d67adee6f99113f525b376da4af27c3001321 --- docs/tutorial/templates.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorial/templates.rst b/docs/tutorial/templates.rst index 4cb7db7f..4f7977e8 100644 --- a/docs/tutorial/templates.rst +++ b/docs/tutorial/templates.rst @@ -79,9 +79,9 @@ HTTP method: {% endif %}
      {% for entry in entries %} -
    • {{ entry.title }}

      {{ entry.text|safe }} +
    • {{ entry.title }}

      {{ entry.text|safe }}
    • {% else %} -
    • Unbelievable. No entries here so far +
    • Unbelievable. No entries here so far
    • {% endfor %}
    {% endblock %} From f5adb61b28f240effbba5a4686647c2af6e85b94 Mon Sep 17 00:00:00 2001 From: Static Date: Mon, 6 Mar 2017 07:05:59 -0600 Subject: [PATCH 110/399] Fix typos/grammar in docs (#2201) --- docs/conf.py | 2 +- docs/patterns/fileuploads.rst | 2 +- docs/styleguide.rst | 2 +- docs/upgrading.rst | 4 ++-- flask/app.py | 4 ++-- scripts/flask-07-upgrade.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 8682dd8c..f53d72fa 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -234,7 +234,7 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf'] # The scheme of the identifier. Typical schemes are ISBN or URL. #epub_scheme = '' -# The unique identifier of the text. This can be a ISBN number +# The unique identifier of the text. This can be an ISBN number # or the project homepage. #epub_identifier = '' diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index 8bf2287c..3a42d325 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -58,7 +58,7 @@ the file and redirects the user to the URL for the uploaded file:: return redirect(request.url) file = request.files['file'] # if user does not select file, browser also - # submit a empty part without filename + # submit an empty part without filename if file.filename == '': flash('No selected file') return redirect(request.url) diff --git a/docs/styleguide.rst b/docs/styleguide.rst index e03e4ef5..390d5668 100644 --- a/docs/styleguide.rst +++ b/docs/styleguide.rst @@ -167,7 +167,7 @@ Docstring conventions: """ Module header: - The module header consists of an utf-8 encoding declaration (if non + The module header consists of a utf-8 encoding declaration (if non ASCII letters are used, but it is recommended all the time) and a standard docstring:: diff --git a/docs/upgrading.rst b/docs/upgrading.rst index 436b0430..af2383c0 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -49,7 +49,7 @@ Any of the following is functionally equivalent:: response = send_file(open(fname), attachment_filename=fname) response.set_etag(...) -The reason for this is that some file-like objects have a invalid or even +The reason for this is that some file-like objects have an invalid or even misleading ``name`` attribute. Silently swallowing errors in such cases was not a satisfying solution. @@ -198,7 +198,7 @@ applications with Flask. Because we want to make upgrading as easy as possible we tried to counter the problems arising from these changes by providing a script that can ease the transition. -The script scans your whole application and generates an unified diff with +The script scans your whole application and generates a unified diff with changes it assumes are safe to apply. However as this is an automated tool it won't be able to find all use cases and it might miss some. We internally spread a lot of deprecation warnings all over the place to make diff --git a/flask/app.py b/flask/app.py index 27918d01..7745ace6 100644 --- a/flask/app.py +++ b/flask/app.py @@ -391,7 +391,7 @@ class Flask(_PackageBoundObject): #: is the class for the instance check and the second the error handler #: function. #: - #: To register a error handler, use the :meth:`errorhandler` + #: To register an error handler, use the :meth:`errorhandler` #: decorator. self.error_handler_spec = {None: self._error_handlers} @@ -1354,7 +1354,7 @@ class Flask(_PackageBoundObject): will have to surround the execution of these code by try/except statements and log occurring errors. - When a teardown function was called because of a exception it will + When a teardown function was called because of an exception it will be passed an error object. The return values of teardown functions are ignored. diff --git a/scripts/flask-07-upgrade.py b/scripts/flask-07-upgrade.py index 7fbdd49c..18e1a14b 100644 --- a/scripts/flask-07-upgrade.py +++ b/scripts/flask-07-upgrade.py @@ -5,7 +5,7 @@ ~~~~~~~~~~~~~~~~ This command line script scans a whole application tree and attempts to - output an unified diff with all the changes that are necessary to easily + output a unified diff with all the changes that are necessary to easily upgrade the application to 0.7 and to not yield deprecation warnings. This will also attempt to find `after_request` functions that don't modify From a7f1a21c1204828388eaed1e3903a74c904c8147 Mon Sep 17 00:00:00 2001 From: Hsiaoming Yang Date: Tue, 7 Mar 2017 10:09:46 +0900 Subject: [PATCH 111/399] Don't rely on X-Requested-With for pretty print json response (#2193) * Don't rely on X-Requested-With for pretty print json response * Fix test cases for pretty print json patch * Fix gramma error in docs for pretty print json config * Add changelog for JSONIFY_PRETTYPRINT_REGULAR --- CHANGES | 3 +++ docs/config.rst | 8 +++----- flask/app.py | 2 +- flask/json.py | 2 +- tests/test_basic.py | 2 +- tests/test_helpers.py | 2 ++ 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 2c0a4869..7c50d0c7 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,9 @@ Major release, unreleased - Make `app.run()` into a noop if a Flask application is run from the development server on the command line. This avoids some behavior that was confusing to debug for newcomers. +- Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify() + method returns compressed response by default, and pretty response in + debug mode. Version 0.12.1 -------------- diff --git a/docs/config.rst b/docs/config.rst index c36cc852..75ce239a 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -194,11 +194,9 @@ The following configuration values are used internally by Flask: This is not recommended but might give you a performance improvement on the cost of cacheability. -``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` (the default) - jsonify responses will be pretty printed - if they are not requested by an - XMLHttpRequest object (controlled by - the ``X-Requested-With`` header) +``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` or the Flask app + is running in debug mode, jsonify responses + will be pretty printed. ``JSONIFY_MIMETYPE`` MIME type used for jsonify responses. ``TEMPLATES_AUTO_RELOAD`` Whether to check for modifications of the template source and reload it diff --git a/flask/app.py b/flask/app.py index 7745ace6..bc38d9ea 100644 --- a/flask/app.py +++ b/flask/app.py @@ -314,7 +314,7 @@ class Flask(_PackageBoundObject): 'PREFERRED_URL_SCHEME': 'http', 'JSON_AS_ASCII': True, 'JSON_SORT_KEYS': True, - 'JSONIFY_PRETTYPRINT_REGULAR': True, + 'JSONIFY_PRETTYPRINT_REGULAR': False, 'JSONIFY_MIMETYPE': 'application/json', 'TEMPLATES_AUTO_RELOAD': None, }) diff --git a/flask/json.py b/flask/json.py index 19b337c3..0ba9d717 100644 --- a/flask/json.py +++ b/flask/json.py @@ -248,7 +248,7 @@ def jsonify(*args, **kwargs): indent = None separators = (',', ':') - if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] and not request.is_xhr: + if current_app.config['JSONIFY_PRETTYPRINT_REGULAR'] or current_app.debug: indent = 2 separators = (', ', ': ') diff --git a/tests/test_basic.py b/tests/test_basic.py index 6341234b..942eb0f6 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -995,7 +995,7 @@ def test_make_response_with_response_instance(): rv = flask.make_response( flask.jsonify({'msg': 'W00t'}), 400) assert rv.status_code == 400 - assert rv.data == b'{\n "msg": "W00t"\n}\n' + assert rv.data == b'{"msg":"W00t"}\n' assert rv.mimetype == 'application/json' rv = flask.make_response( diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 3e2ea8cd..fd448fb8 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -289,6 +289,8 @@ class TestJSON(object): def test_json_key_sorting(self): app = flask.Flask(__name__) app.testing = True + app.debug = True + assert app.config['JSON_SORT_KEYS'] == True d = dict.fromkeys(range(20), 'foo') From 7a5e8ef38e0f4f110b7739253308a3356b13b0de Mon Sep 17 00:00:00 2001 From: Ben Date: Wed, 8 Mar 2017 11:26:38 -0800 Subject: [PATCH 112/399] Fix broken link (#2202) --- docs/patterns/distribute.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/distribute.rst b/docs/patterns/distribute.rst index 72cc25d6..f4a07579 100644 --- a/docs/patterns/distribute.rst +++ b/docs/patterns/distribute.rst @@ -174,4 +174,4 @@ the code without having to run ``install`` again after each change. .. _pip: https://pypi.python.org/pypi/pip -.. _Setuptools: https://pythonhosted.org/setuptools +.. _Setuptools: https://pypi.python.org/pypi/setuptools From 46e8427d814589145ffcdc10cce45b791bde795b Mon Sep 17 00:00:00 2001 From: John Bodley Date: Sat, 11 Mar 2017 09:59:34 -0800 Subject: [PATCH 113/399] Document run() host defaulting to SERVER_NAME --- flask/app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index bc38d9ea..c8540b5f 100644 --- a/flask/app.py +++ b/flask/app.py @@ -813,7 +813,8 @@ class Flask(_PackageBoundObject): :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to have the server available externally as well. Defaults to - ``'127.0.0.1'``. + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config + variable if present. :param port: the port of the webserver. Defaults to ``5000`` or the port defined in the ``SERVER_NAME`` config variable if present. From 1add1f8a02976e070660d9ae0b877bc3f8a36e86 Mon Sep 17 00:00:00 2001 From: Jan Ferko Date: Mon, 13 Mar 2017 13:58:24 +0100 Subject: [PATCH 114/399] Use print function in quickstart (#2204) Example in URL Building section uses `print` statement instead of `print` function, which causes syntax error when example is run on Python 3. --- docs/quickstart.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7ce8a90f..09365496 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -264,10 +264,10 @@ some examples:: ... def profile(username): pass ... >>> with app.test_request_context(): - ... print url_for('index') - ... print url_for('login') - ... print url_for('login', next='/') - ... print url_for('profile', username='John Doe') + ... print(url_for('index')) + ... print(url_for('login')) + ... print(url_for('login', next='/')) + ... print(url_for('profile', username='John Doe')) ... / /login From 5b7fd9ad889e54d4d694d310b559c921d7df75cf Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Thu, 16 Mar 2017 14:37:58 +0100 Subject: [PATCH 115/399] Print a stacktrace on CLI error (closes #2208) --- flask/cli.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flask/cli.py b/flask/cli.py index bde5a13b..8f8fac03 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -11,6 +11,7 @@ import os import sys +import traceback from threading import Lock, Thread from functools import update_wrapper @@ -368,6 +369,9 @@ class FlaskGroup(AppGroup): # 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. + # However, we will not do so silently because that would confuse + # users. + traceback.print_exc() pass return sorted(rv) From ad42d88fb2cda12a21c4fb6f002f425f233d1fe3 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Thu, 16 Mar 2017 14:42:09 +0100 Subject: [PATCH 116/399] Remove useless pass --- flask/cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index 8f8fac03..8db7e07e 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -372,7 +372,6 @@ class FlaskGroup(AppGroup): # However, we will not do so silently because that would confuse # users. traceback.print_exc() - pass return sorted(rv) def main(self, *args, **kwargs): From ed17bc171046a15f03c890687db8eb9652513bd9 Mon Sep 17 00:00:00 2001 From: Sven-Hendrik Haase Date: Thu, 16 Mar 2017 20:56:12 +0100 Subject: [PATCH 117/399] Add test to showcase that printing a traceback works --- tests/test_cli.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_cli.py b/tests/test_cli.py index 313a34d2..82c69f93 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -191,3 +191,20 @@ def test_flaskgroup(): result = runner.invoke(cli, ['test']) assert result.exit_code == 0 assert result.output == 'flaskgroup\n' + + +def test_print_exceptions(): + """Print the stacktrace if the CLI.""" + def create_app(info): + raise Exception("oh no") + return Flask("flaskgroup") + + @click.group(cls=FlaskGroup, create_app=create_app) + def cli(**params): + pass + + runner = CliRunner() + result = runner.invoke(cli, ['--help']) + assert result.exit_code == 0 + assert 'Exception: oh no' in result.output + assert 'Traceback' in result.output From 2995366dde63b8acd1f246bcd5a4cf7d61f0c1fa Mon Sep 17 00:00:00 2001 From: Larivact Date: Fri, 17 Mar 2017 05:41:20 +0100 Subject: [PATCH 118/399] Clarify APPLICATION_ROOT #1714 --- docs/config.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 75ce239a..714b54c8 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -132,13 +132,13 @@ The following configuration values are used internally by Flask: by default enables URL generation without a request context but with an application context. -``APPLICATION_ROOT`` If the application does not occupy - a whole domain or subdomain this can - be set to the path where the application - is configured to live. This is for - session cookie as path value. If - domains are used, this should be - ``None``. +``APPLICATION_ROOT`` The path value used for the session + cookie if ``SESSION_COOKIE_PATH`` isn't + set. If it's also ``None`` ``'/'`` is used. + Note that to actually serve your Flask + app under a subpath you need to tell + your WSGI container the ``SCRIPT_NAME`` + WSGI environment variable. ``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will reject incoming requests with a content length greater than this by From 889c0ed1964f48557e532f95f9003c5ee6e2b727 Mon Sep 17 00:00:00 2001 From: Runar Trollet Kristoffersen Date: Sun, 19 Mar 2017 18:01:23 +0100 Subject: [PATCH 119/399] Issue #2212: documentation: virtualenv and python3 --- docs/installation.rst | 41 +++++++++++++++++++++++++++++++++-------- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 96c363f5..38094ded 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -40,6 +40,12 @@ installations of Python, one for each project. It doesn't actually install separate copies of Python, but it does provide a clever way to keep different project environments isolated. Let's see how virtualenv works. + +.. admonition:: A note on python3 and virtualenv + + If you are planning on using python3 with the virtualenv, you don't need to + install ``virtualenv``. Python3 has built-in support for virtual environments. + If you are on Mac OS X or Linux, chances are that the following command will work for you:: @@ -55,24 +61,43 @@ install it first. Check the :ref:`windows-easy-install` section for more information about how to do that. Once you have it installed, run the same commands as above, but without the ``sudo`` prefix. +Creating a virtual environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Once you have virtualenv installed, just fire up a shell and create -your own environment. I usually create a project folder and a :file:`venv` +your own environment. I usually create a project folder and a :file:`virtenv` folder within:: $ mkdir myproject $ cd myproject - $ virtualenv venv - New python executable in venv/bin/python + +There is a little change in how you create a virtualenv depending on which python-version you are currently using. + +**Python2** + +:: + + $ virtualenv virtenv + New python executable in virtenv/bin/python Installing setuptools, pip............done. +**Python 3.6 and above** + +:: + + $ python3 -m venv virtenv + +Activating a virtual environment +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + 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:: - $ . venv/bin/activate + $ . virtenv/bin/activate If you are a Windows user, the following command is for you:: - $ venv\Scripts\activate + $ virtenv\Scripts\activate Either way, you should now be using your virtualenv (notice how the prompt of your shell has changed to show the active environment). @@ -115,10 +140,10 @@ Get the git checkout in a new virtualenv and run in development mode:: $ git clone https://github.com/pallets/flask.git Initialized empty Git repository in ~/dev/flask/.git/ $ cd flask - $ virtualenv venv - New python executable in venv/bin/python + $ virtualenv virtenv + New python executable in virtenv/bin/python Installing setuptools, pip............done. - $ . venv/bin/activate + $ . virtenv/bin/activate $ python setup.py develop ... Finished processing dependencies for Flask From 7a7a163ff18c4491b8c2a6cd0630a6f4e4ce2984 Mon Sep 17 00:00:00 2001 From: Ed Brannin Date: Tue, 21 Mar 2017 15:22:15 -0400 Subject: [PATCH 120/399] shorten output when ImportError due to app bug. Before: ``` C:\dev\tmp>py -2 -m flask run Traceback (most recent call last): File "C:\Python27\lib\runpy.py", line 174, in _run_module_as_main "__main__", fname, loader, pkg_name) File "C:\Python27\lib\runpy.py", line 72, in _run_code exec code in run_globals File "c:\dev\sourcetree\flask\flask\__main__.py", line 15, in main(as_module=True) File "c:\dev\sourcetree\flask\flask\cli.py", line 523, in main cli.main(args=args, prog_name=name) File "c:\dev\sourcetree\flask\flask\cli.py", line 383, in main return AppGroup.main(self, *args, **kwargs) File "C:\Python27\lib\site-packages\click\core.py", line 697, in main rv = self.invoke(ctx) File "C:\Python27\lib\site-packages\click\core.py", line 1066, in invoke return _process_result(sub_ctx.command.invoke(sub_ctx)) File "C:\Python27\lib\site-packages\click\core.py", line 895, in invoke return ctx.invoke(self.callback, **ctx.params) File "C:\Python27\lib\site-packages\click\core.py", line 535, in invoke return callback(*args, **kwargs) File "C:\Python27\lib\site-packages\click\decorators.py", line 64, in new_func return ctx.invoke(f, obj, *args[1:], **kwargs) File "C:\Python27\lib\site-packages\click\core.py", line 535, in invoke return callback(*args, **kwargs) File "c:\dev\sourcetree\flask\flask\cli.py", line 433, in run_command app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) File "c:\dev\sourcetree\flask\flask\cli.py", line 153, in __init__ self._load_unlocked() File "c:\dev\sourcetree\flask\flask\cli.py", line 177, in _load_unlocked self._app = rv = self.loader() File "c:\dev\sourcetree\flask\flask\cli.py", line 238, in load_app rv = locate_app(self.app_import_path) File "c:\dev\sourcetree\flask\flask\cli.py", line 91, in locate_app __import__(module) File "C:\dev\tmp\error.py", line 1, in import whatisthisidonteven ImportError: No module named whatisthisidonteven ``` After: ``` C:\dev\tmp>py -2 -m flask run Usage: python -m flask run [OPTIONS] Error: There was an error trying to import the app (error): Traceback (most recent call last): File "c:\dev\sourcetree\flask\flask\cli.py", line 91, in locate_app __import__(module) File "C:\dev\tmp\error.py", line 1, in import whatisthisidonteven ImportError: No module named whatisthisidonteven``` --- flask/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index 8db7e07e..0cc240a2 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -93,7 +93,9 @@ def locate_app(app_id): # Reraise the ImportError if it occurred within the imported module. # Determine this by checking whether the trace has a depth > 1. if sys.exc_info()[-1].tb_next: - raise + stack_trace = traceback.format_exc() + raise NoAppException('There was an error trying to import' + ' the app (%s):\n%s' % (module, stack_trace)) else: raise NoAppException('The file/path provided (%s) does not appear' ' to exist. Please verify the path is ' From 6e5250ab5dcdbf1e6d47e8481ba80de4f44f20f9 Mon Sep 17 00:00:00 2001 From: Ed Brannin Date: Tue, 21 Mar 2017 16:17:09 -0400 Subject: [PATCH 121/399] Fix CLI test for ImportError -> NoAppException --- tests/test_cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 82c69f93..8b291a63 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -83,7 +83,7 @@ def test_locate_app(test_apps): pytest.raises(NoAppException, locate_app, "notanpp.py") pytest.raises(NoAppException, locate_app, "cliapp/app") pytest.raises(RuntimeError, locate_app, "cliapp.app:notanapp") - pytest.raises(ImportError, locate_app, "cliapp.importerrorapp") + pytest.raises(NoAppException, locate_app, "cliapp.importerrorapp") def test_find_default_import_path(test_apps, monkeypatch, tmpdir): From 0049922f2e690a6d58f335ca9196c95f1de84370 Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Thu, 23 Mar 2017 17:30:48 +0100 Subject: [PATCH 122/399] Fix send_file to work with non-ascii filenames This commit implements https://tools.ietf.org/html/rfc2231#section-4 in order to support sending unicode characters. Tested on both Firefox and Chromium under Linux. This adds unidecode as a dependency, which might be relaxed by using .encode('latin-1', 'ignore') but wouldn't be as useful. Also, added a test for the correct headers to be added. Previously, using a filename parameter to send_file with unicode characters, it failed with the next error since HTTP headers don't allow non latin-1 characters. Error on request: Traceback (most recent call last): File "/usr/lib/python3.6/site-packages/werkzeug/serving.py", line 193, in run_wsgi execute(self.server.app) File "/usr/lib/python3.6/site-packages/werkzeug/serving.py", line 186, in execute write(b'') File "/usr/lib/python3.6/site-packages/werkzeug/serving.py", line 152, in write self.send_header(key, value) File "/usr/lib64/python3.6/http/server.py", line 509, in send_header ("%s: %s\r\n" % (keyword, value)).encode('latin-1', 'strict')) UnicodeEncodeError: 'latin-1' codec can't encode character '\uff0f' in position 58: ordinal not in range(256) Fixes #1286 --- flask/helpers.py | 6 +++++- setup.py | 1 + tests/test_helpers.py | 11 +++++++++++ tox.ini | 1 + 4 files changed, 18 insertions(+), 1 deletion(-) diff --git a/flask/helpers.py b/flask/helpers.py index 2f446327..b2ea2ce9 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -41,6 +41,7 @@ from .signals import message_flashed from .globals import session, _request_ctx_stack, _app_ctx_stack, \ current_app, request from ._compat import string_types, text_type +from unidecode import unidecode # sentinel @@ -534,8 +535,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, if attachment_filename is None: raise TypeError('filename unavailable, required for ' 'sending as attachment') + filename_dict = { + 'filename': unidecode(attachment_filename), + 'filename*': "UTF-8''%s" % url_quote(attachment_filename)} headers.add('Content-Disposition', 'attachment', - filename=attachment_filename) + **filename_dict) if current_app.use_x_sendfile and filename: if file is not None: diff --git a/setup.py b/setup.py index 08995073..2ffe72ed 100644 --- a/setup.py +++ b/setup.py @@ -75,6 +75,7 @@ setup( 'Jinja2>=2.4', 'itsdangerous>=0.21', 'click>=2.0', + 'unidecode', ], classifiers=[ 'Development Status :: 4 - Beta', diff --git a/tests/test_helpers.py b/tests/test_helpers.py index fd448fb8..362b6cfa 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -560,6 +560,17 @@ class TestSendfile(object): assert options['filename'] == 'index.txt' rv.close() + def test_attachment_with_utf8_filename(self): + app = flask.Flask(__name__) + with app.test_request_context(): + with open(os.path.join(app.root_path, 'static/index.html')) as f: + rv = flask.send_file(f, as_attachment=True, + attachment_filename='Ñandú/pingüino.txt') + value, options = \ + parse_options_header(rv.headers['Content-Disposition']) + assert options == {'filename': 'Nandu/pinguino.txt', 'filename*': "UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt"} + rv.close() + def test_static_file(self): app = flask.Flask(__name__) # default cache timeout is 12 hours diff --git a/tox.ini b/tox.ini index 764b4030..3db7b91f 100644 --- a/tox.ini +++ b/tox.ini @@ -27,6 +27,7 @@ deps= devel: git+https://github.com/pallets/itsdangerous.git devel: git+https://github.com/jek/blinker.git simplejson: simplejson + unidecode [testenv:docs] deps = sphinx From 6ef45f30ab0e95d80ee2a29168f098e9037b9e0b Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Fri, 24 Mar 2017 20:05:01 +0100 Subject: [PATCH 123/399] Fix previous commits to work with python 2 and python 3 Also, parse_options_header seems to interpret filename* so we better test the actual value used in the headers (and since it's valid in any order, use a set to compare) --- flask/helpers.py | 2 +- tests/test_helpers.py | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index b2ea2ce9..e4fb8c43 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -536,7 +536,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, raise TypeError('filename unavailable, required for ' 'sending as attachment') filename_dict = { - 'filename': unidecode(attachment_filename), + 'filename': unidecode(text_type(attachment_filename)), 'filename*': "UTF-8''%s" % url_quote(attachment_filename)} headers.add('Content-Disposition', 'attachment', **filename_dict) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 362b6cfa..f7affb2c 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -565,10 +565,11 @@ class TestSendfile(object): with app.test_request_context(): with open(os.path.join(app.root_path, 'static/index.html')) as f: rv = flask.send_file(f, as_attachment=True, - attachment_filename='Ñandú/pingüino.txt') - value, options = \ - parse_options_header(rv.headers['Content-Disposition']) - assert options == {'filename': 'Nandu/pinguino.txt', 'filename*': "UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt"} + attachment_filename=u'Ñandú/pingüino.txt') + content_disposition = set(rv.headers['Content-Disposition'].split(';')) + assert content_disposition == set(['attachment', + ' filename="Nandu/pinguino.txt"', + " filename*=UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt"]) rv.close() def test_static_file(self): From bf023e7dc0bae78ff0ab14dc9f10a87e2b56f676 Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Thu, 30 Mar 2017 17:32:21 +0200 Subject: [PATCH 124/399] Remove unidecode dependency and use unicodedata instead I found a way to remove the unidecode dependency without sacrificing much by using unicodedata.normalize . --- flask/helpers.py | 6 ++++-- setup.py | 1 - tox.ini | 1 - 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index e4fb8c43..aa8be315 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -14,6 +14,7 @@ import sys import pkgutil import posixpath import mimetypes +import unicodedata from time import time from zlib import adler32 from threading import RLock @@ -41,7 +42,6 @@ from .signals import message_flashed from .globals import session, _request_ctx_stack, _app_ctx_stack, \ current_app, request from ._compat import string_types, text_type -from unidecode import unidecode # sentinel @@ -536,7 +536,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, raise TypeError('filename unavailable, required for ' 'sending as attachment') filename_dict = { - 'filename': unidecode(text_type(attachment_filename)), + 'filename': (unicodedata.normalize('NFKD', + text_type(attachment_filename)).encode('ascii', + 'ignore')), 'filename*': "UTF-8''%s" % url_quote(attachment_filename)} headers.add('Content-Disposition', 'attachment', **filename_dict) diff --git a/setup.py b/setup.py index 2ffe72ed..08995073 100644 --- a/setup.py +++ b/setup.py @@ -75,7 +75,6 @@ setup( 'Jinja2>=2.4', 'itsdangerous>=0.21', 'click>=2.0', - 'unidecode', ], classifiers=[ 'Development Status :: 4 - Beta', diff --git a/tox.ini b/tox.ini index 3db7b91f..764b4030 100644 --- a/tox.ini +++ b/tox.ini @@ -27,7 +27,6 @@ deps= devel: git+https://github.com/pallets/itsdangerous.git devel: git+https://github.com/jek/blinker.git simplejson: simplejson - unidecode [testenv:docs] deps = sphinx From 1d4448abe335741c61b3c8c5f99e1607a13f7e3d Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Fri, 31 Mar 2017 17:07:43 +0100 Subject: [PATCH 125/399] Handle BaseExceptions (#2222) * Handle BaseExceptions * Add test and changes * Make test more idiomatic --- CHANGES | 3 +++ flask/app.py | 3 +++ tests/test_basic.py | 17 +++++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/CHANGES b/CHANGES index 7c50d0c7..6933c0c9 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,9 @@ Major release, unreleased - Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify() method returns compressed response by default, and pretty response in debug mode. +- Call `ctx.auto_pop` with the exception object instead of `None`, in the + event that a `BaseException` such as `KeyboardInterrupt` is raised in a + request handler. Version 0.12.1 -------------- diff --git a/flask/app.py b/flask/app.py index c8540b5f..6617b02b 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1991,6 +1991,9 @@ class Flask(_PackageBoundObject): except Exception as e: error = e response = self.handle_exception(e) + except: + error = sys.exc_info()[1] + raise return response(environ, start_response) finally: if self.should_ignore_error(error): diff --git a/tests/test_basic.py b/tests/test_basic.py index 942eb0f6..ffc12dc1 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -791,6 +791,23 @@ def test_error_handling_processing(): assert resp.data == b'internal server error' +def test_baseexception_error_handling(): + app = flask.Flask(__name__) + app.config['LOGGER_HANDLER_POLICY'] = 'never' + + @app.route('/') + def broken_func(): + raise KeyboardInterrupt() + + with app.test_client() as c: + with pytest.raises(KeyboardInterrupt): + c.get('/') + + ctx = flask._request_ctx_stack.top + assert ctx.preserved + assert type(ctx._preserved_exc) is KeyboardInterrupt + + def test_before_request_and_routing_errors(): app = flask.Flask(__name__) From 12c49c75fbd04cfe81808ef300fcb0858d92c7b7 Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Thu, 23 Mar 2017 14:43:56 +0000 Subject: [PATCH 126/399] Handle BaseExceptions --- flask/app.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/flask/app.py b/flask/app.py index 942992dc..1404e17e 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1983,6 +1983,9 @@ class Flask(_PackageBoundObject): except Exception as e: error = e response = self.handle_exception(e) + except: + error = sys.exc_info()[1] + raise return response(environ, start_response) finally: if self.should_ignore_error(error): From d0e2e7b66c2f0a2192ce4b1b54e118345acf7f6e Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Thu, 23 Mar 2017 16:15:00 +0000 Subject: [PATCH 127/399] Add test and changes --- CHANGES | 15 +++++++++++++++ tests/test_basic.py | 20 ++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/CHANGES b/CHANGES index 03194421..dbb5947a 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,21 @@ Flask Changelog Here you can see the full list of changes between each Flask release. +Version 0.13 +------------ + +Major release, unreleased + +- Make `app.run()` into a noop if a Flask application is run from the + development server on the command line. This avoids some behavior that + was confusing to debug for newcomers. +- Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify() + method returns compressed response by default, and pretty response in + debug mode. +- Call `ctx.auto_pop` with the exception object instead of `None`, in the + event that a `BaseException` such as `KeyboardInterrupt` is raised in a + request handler. + Version 0.12.1 -------------- diff --git a/tests/test_basic.py b/tests/test_basic.py index be3d5edd..8556268a 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -791,6 +791,26 @@ def test_error_handling_processing(): assert resp.data == b'internal server error' +def test_baseexception_error_handling(): + app = flask.Flask(__name__) + app.config['LOGGER_HANDLER_POLICY'] = 'never' + + @app.route('/') + def broken_func(): + raise KeyboardInterrupt() + + with app.test_client() as c: + try: + c.get('/') + raise AssertionError("KeyboardInterrupt should have been raised") + except KeyboardInterrupt: + pass + + ctx = flask._request_ctx_stack.top + assert ctx.preserved + assert type(ctx._preserved_exc) is KeyboardInterrupt + + def test_before_request_and_routing_errors(): app = flask.Flask(__name__) From 6f7847e3c488fccbfd8c8880683cfe8ae8bdcdc3 Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Thu, 23 Mar 2017 17:51:45 +0000 Subject: [PATCH 128/399] Make test more idiomatic --- tests/test_basic.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/tests/test_basic.py b/tests/test_basic.py index 8556268a..c5ec9f5c 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -800,11 +800,8 @@ def test_baseexception_error_handling(): raise KeyboardInterrupt() with app.test_client() as c: - try: + with pytest.raises(KeyboardInterrupt): c.get('/') - raise AssertionError("KeyboardInterrupt should have been raised") - except KeyboardInterrupt: - pass ctx = flask._request_ctx_stack.top assert ctx.preserved From 80c7db638cfd1f778cc065a26c6ede85fa1ac4e7 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 31 Mar 2017 18:41:10 +0200 Subject: [PATCH 129/399] Correct changelog --- CHANGES | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index dbb5947a..99ae5803 100644 --- a/CHANGES +++ b/CHANGES @@ -14,9 +14,6 @@ Major release, unreleased - Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify() method returns compressed response by default, and pretty response in debug mode. -- Call `ctx.auto_pop` with the exception object instead of `None`, in the - event that a `BaseException` such as `KeyboardInterrupt` is raised in a - request handler. Version 0.12.1 -------------- @@ -27,6 +24,9 @@ Bugfix release, unreleased within the imported application module. - Fix encoding behavior of ``app.config.from_pyfile`` for Python 3. Fix ``#2118``. +- Call `ctx.auto_pop` with the exception object instead of `None`, in the + event that a `BaseException` such as `KeyboardInterrupt` is raised in a + request handler. Version 0.12 ------------ From f7d6d4d4f60dfbebd80d9bbe9e68ebdd00d89dd6 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 31 Mar 2017 18:43:34 +0200 Subject: [PATCH 130/399] Prepare for 0.12.1 --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 99ae5803..613b8189 100644 --- a/CHANGES +++ b/CHANGES @@ -18,7 +18,7 @@ Major release, unreleased Version 0.12.1 -------------- -Bugfix release, unreleased +Bugfix release, released on March 31st 2017 - Prevent `flask run` from showing a NoAppException when an ImportError occurs within the imported application module. From a34d0e6878c8c8a5fab05a69785c443f3c17075d Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 31 Mar 2017 18:43:36 +0200 Subject: [PATCH 131/399] Bump version number to 0.12.1 --- flask/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__init__.py b/flask/__init__.py index 3cef3b43..2fcb3567 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.12.1-dev' +__version__ = '0.12.1' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. From 07a705888cbdf2641f3686f6d87575ef99093c7e Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 31 Mar 2017 18:43:52 +0200 Subject: [PATCH 132/399] Bump to dev version --- flask/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__init__.py b/flask/__init__.py index 2fcb3567..59d711b8 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.12.1' +__version__ = '0.12.2-dev' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. From c935eaceafebaf167f6422fdbd0de3b6bbb96bdf Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Fri, 31 Mar 2017 18:44:14 +0200 Subject: [PATCH 133/399] Revert "Handle BaseExceptions (#2222)" This reverts commit 1d4448abe335741c61b3c8c5f99e1607a13f7e3d. --- CHANGES | 3 --- flask/app.py | 3 --- tests/test_basic.py | 17 ----------------- 3 files changed, 23 deletions(-) diff --git a/CHANGES b/CHANGES index 6933c0c9..7c50d0c7 100644 --- a/CHANGES +++ b/CHANGES @@ -14,9 +14,6 @@ Major release, unreleased - Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify() method returns compressed response by default, and pretty response in debug mode. -- Call `ctx.auto_pop` with the exception object instead of `None`, in the - event that a `BaseException` such as `KeyboardInterrupt` is raised in a - request handler. Version 0.12.1 -------------- diff --git a/flask/app.py b/flask/app.py index 6617b02b..c8540b5f 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1991,9 +1991,6 @@ class Flask(_PackageBoundObject): except Exception as e: error = e response = self.handle_exception(e) - except: - error = sys.exc_info()[1] - raise return response(environ, start_response) finally: if self.should_ignore_error(error): diff --git a/tests/test_basic.py b/tests/test_basic.py index ffc12dc1..942eb0f6 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -791,23 +791,6 @@ def test_error_handling_processing(): assert resp.data == b'internal server error' -def test_baseexception_error_handling(): - app = flask.Flask(__name__) - app.config['LOGGER_HANDLER_POLICY'] = 'never' - - @app.route('/') - def broken_func(): - raise KeyboardInterrupt() - - with app.test_client() as c: - with pytest.raises(KeyboardInterrupt): - c.get('/') - - ctx = flask._request_ctx_stack.top - assert ctx.preserved - assert type(ctx._preserved_exc) is KeyboardInterrupt - - def test_before_request_and_routing_errors(): app = flask.Flask(__name__) From 8cf32bca519723c5d97f123127cdec81dba868f6 Mon Sep 17 00:00:00 2001 From: Kyle Lawlor Date: Sat, 1 Apr 2017 20:27:28 -0400 Subject: [PATCH 134/399] Adds in blueprints and an application factory --- examples/flaskr/flaskr/__init__.py | 1 - examples/flaskr/flaskr/_cliapp.py | 3 + examples/flaskr/flaskr/blueprints/__init__.py | 0 .../flaskr/flaskr/{ => blueprints}/flaskr.py | 55 +++++-------------- examples/flaskr/flaskr/factory.py | 52 ++++++++++++++++++ examples/flaskr/flaskr/templates/layout.html | 4 +- examples/flaskr/flaskr/templates/login.html | 2 +- .../flaskr/flaskr/templates/show_entries.html | 2 +- examples/flaskr/setup.py | 4 +- examples/flaskr/tests/test_flaskr.py | 34 +++++++----- 10 files changed, 95 insertions(+), 62 deletions(-) create mode 100644 examples/flaskr/flaskr/_cliapp.py create mode 100644 examples/flaskr/flaskr/blueprints/__init__.py rename examples/flaskr/flaskr/{ => blueprints}/flaskr.py (55%) create mode 100644 examples/flaskr/flaskr/factory.py diff --git a/examples/flaskr/flaskr/__init__.py b/examples/flaskr/flaskr/__init__.py index d096b1e7..e69de29b 100644 --- a/examples/flaskr/flaskr/__init__.py +++ b/examples/flaskr/flaskr/__init__.py @@ -1 +0,0 @@ -from .flaskr import app diff --git a/examples/flaskr/flaskr/_cliapp.py b/examples/flaskr/flaskr/_cliapp.py new file mode 100644 index 00000000..faed660c --- /dev/null +++ b/examples/flaskr/flaskr/_cliapp.py @@ -0,0 +1,3 @@ +from flaskr.factory import create_app + +app = create_app() \ No newline at end of file diff --git a/examples/flaskr/flaskr/blueprints/__init__.py b/examples/flaskr/flaskr/blueprints/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/flaskr/flaskr/flaskr.py b/examples/flaskr/flaskr/blueprints/flaskr.py similarity index 55% rename from examples/flaskr/flaskr/flaskr.py rename to examples/flaskr/flaskr/blueprints/flaskr.py index b4c1d6bd..7b64dd9e 100644 --- a/examples/flaskr/flaskr/flaskr.py +++ b/examples/flaskr/flaskr/blueprints/flaskr.py @@ -10,29 +10,18 @@ :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 +from flask import Blueprint, request, session, g, redirect, url_for, abort, \ + render_template, flash, current_app -# create our little application :) -app = Flask(__name__) - -# 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' -)) -app.config.from_envvar('FLASKR_SETTINGS', silent=True) +# create our blueprint :) +bp = Blueprint('flaskr', __name__) def connect_db(): """Connects to the specific database.""" - rv = sqlite3.connect(app.config['DATABASE']) + rv = sqlite3.connect(current_app.config['DATABASE']) rv.row_factory = sqlite3.Row return rv @@ -40,18 +29,11 @@ def connect_db(): def init_db(): """Initializes the database.""" db = get_db() - with app.open_resource('schema.sql', mode='r') as f: + with current_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 get_db(): """Opens a new database connection if there is none yet for the current application context. @@ -61,14 +43,7 @@ def get_db(): return g.sqlite_db -@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() - - -@app.route('/') +@bp.route('/') def show_entries(): db = get_db() cur = db.execute('select title, text from entries order by id desc') @@ -76,7 +51,7 @@ def show_entries(): return render_template('show_entries.html', entries=entries) -@app.route('/add', methods=['POST']) +@bp.route('/add', methods=['POST']) def add_entry(): if not session.get('logged_in'): abort(401) @@ -85,26 +60,26 @@ def add_entry(): [request.form['title'], request.form['text']]) db.commit() flash('New entry was successfully posted') - return redirect(url_for('show_entries')) + return redirect(url_for('flaskr.show_entries')) -@app.route('/login', methods=['GET', 'POST']) +@bp.route('/login', methods=['GET', 'POST']) def login(): error = None if request.method == 'POST': - if request.form['username'] != app.config['USERNAME']: + if request.form['username'] != current_app.config['USERNAME']: error = 'Invalid username' - elif request.form['password'] != app.config['PASSWORD']: + elif request.form['password'] != current_app.config['PASSWORD']: error = 'Invalid password' else: session['logged_in'] = True flash('You were logged in') - return redirect(url_for('show_entries')) + return redirect(url_for('flaskr.show_entries')) return render_template('login.html', error=error) -@app.route('/logout') +@bp.route('/logout') def logout(): session.pop('logged_in', None) flash('You were logged out') - return redirect(url_for('show_entries')) + return redirect(url_for('flaskr.show_entries')) diff --git a/examples/flaskr/flaskr/factory.py b/examples/flaskr/flaskr/factory.py new file mode 100644 index 00000000..e71a6704 --- /dev/null +++ b/examples/flaskr/flaskr/factory.py @@ -0,0 +1,52 @@ +import os +from flask import Flask, g +from werkzeug.utils import find_modules, import_string +from flaskr.blueprints.flaskr import init_db + + +def create_app(config=None): + app = Flask(__name__) + + app.config.update(dict( + DATABASE=os.path.join(app.root_path, 'flaskr.db'), + DEBUG=True, + SECRET_KEY='development key', + USERNAME='admin', + PASSWORD='default' + )) + app.config.update(config or {}) + app.config.from_envvar('FLASKR_SETTINGS', silent=True) + + register_blueprints(app) + register_cli(app) + register_teardowns(app) + + return app + + +def register_blueprints(app): + """Register all blueprint modules + + Reference: Armin Ronacher, "Flask for Fun and for Profit" PyBay 2016. + """ + for name in find_modules('flaskr.blueprints'): + mod = import_string(name) + if hasattr(mod, 'bp'): + app.register_blueprint(mod.bp) + return None + + +def register_cli(app): + @app.cli.command('initdb') + def initdb_command(): + """Creates the database tables.""" + init_db() + print('Initialized the database.') + + +def register_teardowns(app): + @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() \ No newline at end of file diff --git a/examples/flaskr/flaskr/templates/layout.html b/examples/flaskr/flaskr/templates/layout.html index 737b51b2..862a9f4a 100644 --- a/examples/flaskr/flaskr/templates/layout.html +++ b/examples/flaskr/flaskr/templates/layout.html @@ -5,9 +5,9 @@

    Flaskr

    {% if not session.logged_in %} - log in + log in {% else %} - log out + log out {% endif %}
    {% for message in get_flashed_messages() %} diff --git a/examples/flaskr/flaskr/templates/login.html b/examples/flaskr/flaskr/templates/login.html index ed09aeba..505d2f66 100644 --- a/examples/flaskr/flaskr/templates/login.html +++ b/examples/flaskr/flaskr/templates/login.html @@ -2,7 +2,7 @@ {% block body %}

    Login

    {% if error %}

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

    +
    Username:
    diff --git a/examples/flaskr/flaskr/templates/show_entries.html b/examples/flaskr/flaskr/templates/show_entries.html index 2f68b9d3..cf8fbb86 100644 --- a/examples/flaskr/flaskr/templates/show_entries.html +++ b/examples/flaskr/flaskr/templates/show_entries.html @@ -1,7 +1,7 @@ {% extends "layout.html" %} {% block body %} {% if session.logged_in %} - +
    Title:
    diff --git a/examples/flaskr/setup.py b/examples/flaskr/setup.py index 910f23ac..f52aacd8 100644 --- a/examples/flaskr/setup.py +++ b/examples/flaskr/setup.py @@ -1,8 +1,8 @@ -from setuptools import setup +from setuptools import setup, find_packages setup( name='flaskr', - packages=['flaskr'], + packages=find_packages(), include_package_data=True, install_requires=[ 'flask', diff --git a/examples/flaskr/tests/test_flaskr.py b/examples/flaskr/tests/test_flaskr.py index 663e92e0..c8115829 100644 --- a/examples/flaskr/tests/test_flaskr.py +++ b/examples/flaskr/tests/test_flaskr.py @@ -12,20 +12,24 @@ import os import tempfile import pytest -from flaskr import flaskr +from flaskr.factory import create_app +from flaskr.blueprints.flaskr import init_db + + +test_app = create_app() @pytest.fixture def client(request): - db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.config['TESTING'] = True - client = flaskr.app.test_client() - with flaskr.app.app_context(): - flaskr.init_db() + db_fd, test_app.config['DATABASE'] = tempfile.mkstemp() + test_app.config['TESTING'] = True + client = test_app.test_client() + with test_app.app_context(): + init_db() def teardown(): os.close(db_fd) - os.unlink(flaskr.app.config['DATABASE']) + os.unlink(test_app.config['DATABASE']) request.addfinalizer(teardown) return client @@ -50,23 +54,23 @@ def test_empty_db(client): def test_login_logout(client): """Make sure login and logout works""" - rv = login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD']) + rv = login(client, test_app.config['USERNAME'], + test_app.config['PASSWORD']) assert b'You were logged in' in rv.data rv = logout(client) assert b'You were logged out' in rv.data - rv = login(client, flaskr.app.config['USERNAME'] + 'x', - flaskr.app.config['PASSWORD']) + rv = login(client,test_app.config['USERNAME'] + 'x', + test_app.config['PASSWORD']) assert b'Invalid username' in rv.data - rv = login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD'] + 'x') + rv = login(client, test_app.config['USERNAME'], + test_app.config['PASSWORD'] + 'x') assert b'Invalid password' in rv.data def test_messages(client): """Test that messages work""" - login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD']) + login(client, test_app.config['USERNAME'], + test_app.config['PASSWORD']) rv = client.post('/add', data=dict( title='', text='HTML allowed here' From 54b6fc8de689a726aa01a0f2a39ce428e7a69712 Mon Sep 17 00:00:00 2001 From: Kyle Lawlor Date: Sun, 2 Apr 2017 11:55:05 -0400 Subject: [PATCH 135/399] Add in a fixture utilizing app factory --- examples/flaskr/README | 8 +++-- examples/flaskr/flaskr/_cliapp.py | 12 +++++++ examples/flaskr/flaskr/factory.py | 12 +++++++ examples/flaskr/setup.cfg | 4 +-- examples/flaskr/setup.py | 11 +++++++ examples/flaskr/tests/test_flaskr.py | 49 +++++++++++++++++----------- 6 files changed, 72 insertions(+), 24 deletions(-) diff --git a/examples/flaskr/README b/examples/flaskr/README index 90860ff2..22ae857f 100644 --- a/examples/flaskr/README +++ b/examples/flaskr/README @@ -9,9 +9,11 @@ ~ How do I use it? - 1. edit the configuration in the flaskr.py file or + 1. edit the configuration in the factory.py file or export an FLASKR_SETTINGS environment variable - pointing to a configuration file. + pointing to a configuration file or pass in a + dictionary with config values using the create_app + function. 2. install the app from the root of the project directory @@ -19,7 +21,7 @@ 3. Instruct flask to use the right application - export FLASK_APP=flaskr + export FLASK_APP=flaskr._cliapp 4. initialize the database with this command: diff --git a/examples/flaskr/flaskr/_cliapp.py b/examples/flaskr/flaskr/_cliapp.py index faed660c..b13bf79a 100644 --- a/examples/flaskr/flaskr/_cliapp.py +++ b/examples/flaskr/flaskr/_cliapp.py @@ -1,3 +1,15 @@ +# -*- coding: utf-8 -*- +""" + Flaskr + ~~~~~~ + + A microblog example application written as Flask tutorial with + Flask and sqlite3. + + :copyright: (c) 2015 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + from flaskr.factory import create_app app = create_app() \ No newline at end of file diff --git a/examples/flaskr/flaskr/factory.py b/examples/flaskr/flaskr/factory.py index e71a6704..a0f2d235 100644 --- a/examples/flaskr/flaskr/factory.py +++ b/examples/flaskr/flaskr/factory.py @@ -1,3 +1,15 @@ +# -*- coding: utf-8 -*- +""" + Flaskr + ~~~~~~ + + A microblog example application written as Flask tutorial with + Flask and sqlite3. + + :copyright: (c) 2015 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + import os from flask import Flask, g from werkzeug.utils import find_modules, import_string diff --git a/examples/flaskr/setup.cfg b/examples/flaskr/setup.cfg index db50667a..9af7e6f1 100644 --- a/examples/flaskr/setup.cfg +++ b/examples/flaskr/setup.cfg @@ -1,2 +1,2 @@ -[tool:pytest] -test=pytest +[aliases] +test=pytest \ No newline at end of file diff --git a/examples/flaskr/setup.py b/examples/flaskr/setup.py index f52aacd8..7f1dae53 100644 --- a/examples/flaskr/setup.py +++ b/examples/flaskr/setup.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +""" + Flaskr Tests + ~~~~~~~~~~~~ + + Tests the Flaskr application. + + :copyright: (c) 2015 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + from setuptools import setup, find_packages setup( diff --git a/examples/flaskr/tests/test_flaskr.py b/examples/flaskr/tests/test_flaskr.py index c8115829..b5ade2ec 100644 --- a/examples/flaskr/tests/test_flaskr.py +++ b/examples/flaskr/tests/test_flaskr.py @@ -16,20 +16,31 @@ from flaskr.factory import create_app from flaskr.blueprints.flaskr import init_db -test_app = create_app() +@pytest.fixture +def app(request): + db_fd, temp_db_location = tempfile.mkstemp() + config = { + 'DATABASE': temp_db_location, + 'TESTING': True, + 'DB_FD': db_fd + } -@pytest.fixture -def client(request): - db_fd, test_app.config['DATABASE'] = tempfile.mkstemp() - test_app.config['TESTING'] = True - client = test_app.test_client() - with test_app.app_context(): + app = create_app(config=config) + + with app.app_context(): init_db() + yield app + + +@pytest.fixture +def client(request, app): + + client = app.test_client() def teardown(): - os.close(db_fd) - os.unlink(test_app.config['DATABASE']) + os.close(app.config['DB_FD']) + os.unlink(app.config['DATABASE']) request.addfinalizer(teardown) return client @@ -52,25 +63,25 @@ def test_empty_db(client): assert b'No entries here so far' in rv.data -def test_login_logout(client): +def test_login_logout(client, app): """Make sure login and logout works""" - rv = login(client, test_app.config['USERNAME'], - test_app.config['PASSWORD']) + rv = login(client, app.config['USERNAME'], + app.config['PASSWORD']) assert b'You were logged in' in rv.data rv = logout(client) assert b'You were logged out' in rv.data - rv = login(client,test_app.config['USERNAME'] + 'x', - test_app.config['PASSWORD']) + rv = login(client,app.config['USERNAME'] + 'x', + app.config['PASSWORD']) assert b'Invalid username' in rv.data - rv = login(client, test_app.config['USERNAME'], - test_app.config['PASSWORD'] + 'x') + rv = login(client, app.config['USERNAME'], + app.config['PASSWORD'] + 'x') assert b'Invalid password' in rv.data -def test_messages(client): +def test_messages(client, app): """Test that messages work""" - login(client, test_app.config['USERNAME'], - test_app.config['PASSWORD']) + login(client, app.config['USERNAME'], + app.config['PASSWORD']) rv = client.post('/add', data=dict( title='', text='HTML allowed here' From ae1ac2053bcf0c77de32fc7915e36f8ca2f5c961 Mon Sep 17 00:00:00 2001 From: Adam Geitgey Date: Tue, 4 Apr 2017 13:26:40 -0700 Subject: [PATCH 136/399] Correct imports in file upload example (#2230) The example code uses `flash` but doesn't import it. So the code as written doesn't work. This simply adds `flash` to the list of imports in the sample code. --- 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 3a42d325..1c4b0d36 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -21,7 +21,7 @@ specific upload folder and displays a file to the user. Let's look at the bootstrapping code for our application:: import os - from flask import Flask, request, redirect, url_for + from flask import Flask, flash, request, redirect, url_for from werkzeug.utils import secure_filename UPLOAD_FOLDER = '/path/to/the/uploads' From d76d68cd381df4a8c3e2e636c98b5bfa02ab37e4 Mon Sep 17 00:00:00 2001 From: asilversempirical Date: Thu, 6 Apr 2017 11:26:01 -0400 Subject: [PATCH 137/399] Update out of date jsonify documentation https://github.com/pallets/flask/pull/2193 changed the conditions for when jsonify pretty prints, but this comment wasn't updated. --- flask/json.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/flask/json.py b/flask/json.py index 0ba9d717..825dcdf1 100644 --- a/flask/json.py +++ b/flask/json.py @@ -236,11 +236,10 @@ def jsonify(*args, **kwargs): Added support for serializing top-level arrays. This introduces a security risk in ancient browsers. See :ref:`json-security` for details. - This function's response will be pretty printed if it was not requested - with ``X-Requested-With: XMLHttpRequest`` to simplify debugging unless - the ``JSONIFY_PRETTYPRINT_REGULAR`` config parameter is set to false. - Compressed (not pretty) formatting currently means no indents and no - spaces after separators. + This function's response will be pretty printed if the + ``JSONIFY_PRETTYPRINT_REGULAR`` config parameter is set to True or the + Flask app is running in debug mode. Compressed (not pretty) formatting + currently means no indents and no spaces after separators. .. versionadded:: 0.2 """ From ec18fe94775aaa3d54238c6c75b569491803134e Mon Sep 17 00:00:00 2001 From: Grey Li Date: Fri, 7 Apr 2017 22:10:43 +0800 Subject: [PATCH 138/399] Add example for virtualenv integration in cli docs (#2234) --- docs/cli.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/cli.rst b/docs/cli.rst index 2ca0e83e..d0b033f6 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -56,6 +56,18 @@ If you are constantly working with a virtualenv you can also put the bottom of the file. That way every time you activate your virtualenv you automatically also activate the correct application name. +Edit the activate script for the shell you use. For example: + +Unix Bash: ``venv/bin/activate``:: + + FLASK_APP=hello + export FLASK_APP + +Windows CMD.exe: ``venv\Scripts\activate.bat``:: + + set "FLASK_APP=hello" + :END + Debug Flag ---------- From 00d6e339ec789e7b92007297e840a671a5e38a7b Mon Sep 17 00:00:00 2001 From: jab Date: Fri, 7 Apr 2017 10:31:54 -0400 Subject: [PATCH 139/399] Change Flask.__init__ to accept two new keyword arguments, host_matching and static_host. (#1560) This enables host_matching to be set properly by the time the constructor adds the static route, and enables the static route to be properly associated with the required host. Previously, you could only enable host_matching once your app was already instantiated (e.g. app.url_map.host_matching = True), but at that point the constructor would have already added the static route without host matching and an associated host, leaving the static route in a broken state. Fixes #1559. --- AUTHORS | 1 + CHANGES | 5 +++++ flask/app.py | 28 +++++++++++++++++++++------- tests/test_basic.py | 19 +++++++++++++++++++ 4 files changed, 46 insertions(+), 7 deletions(-) diff --git a/AUTHORS b/AUTHORS index cc157dc4..33210243 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Patches and Suggestions - Florent Xicluna - Georg Brandl - Jeff Widman @jeffwidman +- Joshua Bronson @jab - Justin Quick - Kenneth Reitz - Keyan Pishdadian diff --git a/CHANGES b/CHANGES index 9500ee17..440cb260 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,11 @@ Major release, unreleased - Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify() method returns compressed response by default, and pretty response in debug mode. +- Change Flask.__init__ to accept two new keyword arguments, ``host_matching`` + and ``static_host``. This enables ``host_matching`` to be set properly by the + time the constructor adds the static route, and enables the static route to + be properly associated with the required host. (``#1559``) + Version 0.12.1 -------------- diff --git a/flask/app.py b/flask/app.py index 6617b02b..87621aee 100644 --- a/flask/app.py +++ b/flask/app.py @@ -123,6 +123,9 @@ class Flask(_PackageBoundObject): .. versionadded:: 0.11 The `root_path` parameter was added. + .. versionadded:: 0.13 + The `host_matching` and `static_host` parameters were 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 @@ -130,6 +133,13 @@ class Flask(_PackageBoundObject): :param static_folder: the folder with static files that should be served at `static_url_path`. Defaults to the ``'static'`` folder in the root path of the application. + folder in the root path of the application. Defaults + to None. + :param host_matching: sets the app's ``url_map.host_matching`` to the given + given value. Defaults to False. + :param static_host: the host to use when adding the static route. Defaults + to None. Required when using ``host_matching=True`` + with a ``static_folder`` configured. :param template_folder: the folder that contains the templates that should be used by the application. Defaults to ``'templates'`` folder in the root path of the @@ -337,7 +347,8 @@ class Flask(_PackageBoundObject): session_interface = SecureCookieSessionInterface() def __init__(self, import_name, static_path=None, static_url_path=None, - static_folder='static', template_folder='templates', + static_folder='static', static_host=None, + host_matching=False, template_folder='templates', instance_path=None, instance_relative_config=False, root_path=None): _PackageBoundObject.__init__(self, import_name, @@ -525,19 +536,22 @@ class Flask(_PackageBoundObject): #: app.url_map.converters['list'] = ListConverter self.url_map = Map() + self.url_map.host_matching = host_matching + # tracks internally if the application already handled at least one # request. self._got_first_request = False self._before_request_lock = Lock() - # register the static folder for the application. Do that even - # if the folder does not exist. First of all it might be created - # while the server is running (usually happens during development) - # but also because google appengine stores static files somewhere - # else when mapped with the .yml file. + # Add a static route using the provided static_url_path, static_host, + # and static_folder iff there is a configured static_folder. + # Note we do this without checking if static_folder exists. + # For one, it might be created while the server is running (e.g. during + # development). Also, Google App Engine stores static files somewhere if self.has_static_folder: + assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination' self.add_url_rule(self.static_url_path + '/', - endpoint='static', + endpoint='static', host=static_host, view_func=self.send_static_file) #: The click command line context for this application. Commands diff --git a/tests/test_basic.py b/tests/test_basic.py index ffc12dc1..76efd6e2 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1188,6 +1188,25 @@ def test_static_url_path(): assert flask.url_for('static', filename='index.html') == '/foo/index.html' +def test_static_route_with_host_matching(): + app = flask.Flask(__name__, host_matching=True, static_host='example.com') + c = app.test_client() + rv = c.get('http://example.com/static/index.html') + assert rv.status_code == 200 + rv.close() + with app.test_request_context(): + rv = flask.url_for('static', filename='index.html', _external=True) + assert rv == 'http://example.com/static/index.html' + # Providing static_host without host_matching=True should error. + with pytest.raises(Exception): + flask.Flask(__name__, static_host='example.com') + # Providing host_matching=True with static_folder but without static_host should error. + with pytest.raises(Exception): + flask.Flask(__name__, host_matching=True) + # Providing host_matching=True without static_host but with static_folder=None should not error. + flask.Flask(__name__, host_matching=True, static_folder=None) + + def test_none_response(): app = flask.Flask(__name__) app.testing = True From d50a5db5ed07a14733b8cbfc0962b793209014dd Mon Sep 17 00:00:00 2001 From: Antonio Larrosa Date: Fri, 7 Apr 2017 20:34:52 +0200 Subject: [PATCH 140/399] Keep using only filename if it's valid ascii --- flask/helpers.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index aa8be315..19d33420 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -14,7 +14,7 @@ import sys import pkgutil import posixpath import mimetypes -import unicodedata +from unicodedata import normalize from time import time from zlib import adler32 from threading import RLock @@ -535,13 +535,22 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, if attachment_filename is None: raise TypeError('filename unavailable, required for ' 'sending as attachment') - filename_dict = { - 'filename': (unicodedata.normalize('NFKD', - text_type(attachment_filename)).encode('ascii', - 'ignore')), - 'filename*': "UTF-8''%s" % url_quote(attachment_filename)} + normalized = normalize('NFKD', text_type(attachment_filename)) + + try: + normalized.encode('ascii') + except UnicodeEncodeError: + filenames = { + 'filename': normalized.encode('ascii', 'ignore'), + 'filename*': "UTF-8''%s" % url_quote(attachment_filename), + } + else: + filenames = { + 'filename': attachment_filename, + } + headers.add('Content-Disposition', 'attachment', - **filename_dict) + **filenames) if current_app.use_x_sendfile and filename: if file is not None: From c1973016eacfb22fdec837394d8b89c0ca35ad15 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 7 Apr 2017 18:02:31 -0700 Subject: [PATCH 141/399] style cleanup break out header parts in test test for no filename* parameter for ascii header --- flask/helpers.py | 19 ++++++++++++------- tests/test_helpers.py | 25 ++++++++++++++++--------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index 19d33420..420467ad 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -14,10 +14,10 @@ import sys import pkgutil import posixpath import mimetypes -from unicodedata import normalize from time import time from zlib import adler32 from threading import RLock +import unicodedata from werkzeug.routing import BuildError from functools import update_wrapper @@ -478,6 +478,11 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, .. versionchanged:: 0.12 The `attachment_filename` is preferred over `filename` for MIME-type detection. + + .. versionchanged:: 0.13 + UTF-8 filenames, as specified in `RFC 2231`_, are supported. + + .. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4 :param filename_or_fp: the filename of the file to send in `latin-1`. This is relative to the :attr:`~Flask.root_path` @@ -535,7 +540,10 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, if attachment_filename is None: raise TypeError('filename unavailable, required for ' 'sending as attachment') - normalized = normalize('NFKD', text_type(attachment_filename)) + + normalized = unicodedata.normalize( + 'NFKD', text_type(attachment_filename) + ) try: normalized.encode('ascii') @@ -545,12 +553,9 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, 'filename*': "UTF-8''%s" % url_quote(attachment_filename), } else: - filenames = { - 'filename': attachment_filename, - } + filenames = {'filename': attachment_filename} - headers.add('Content-Disposition', 'attachment', - **filenames) + headers.add('Content-Disposition', 'attachment', **filenames) if current_app.use_x_sendfile and filename: if file is not None: diff --git a/tests/test_helpers.py b/tests/test_helpers.py index f7affb2c..1aeff065 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -540,10 +540,11 @@ class TestSendfile(object): value, options = \ parse_options_header(rv.headers['Content-Disposition']) assert value == 'attachment' + assert options['filename'] == 'index.html' + assert 'filename*' not in options rv.close() with app.test_request_context(): - assert options['filename'] == 'index.html' rv = flask.send_file('static/index.html', as_attachment=True) value, options = parse_options_header(rv.headers['Content-Disposition']) assert value == 'attachment' @@ -562,15 +563,21 @@ class TestSendfile(object): def test_attachment_with_utf8_filename(self): app = flask.Flask(__name__) + with app.test_request_context(): - with open(os.path.join(app.root_path, 'static/index.html')) as f: - rv = flask.send_file(f, as_attachment=True, - attachment_filename=u'Ñandú/pingüino.txt') - content_disposition = set(rv.headers['Content-Disposition'].split(';')) - assert content_disposition == set(['attachment', - ' filename="Nandu/pinguino.txt"', - " filename*=UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt"]) - rv.close() + rv = flask.send_file( + 'static/index.html', as_attachment=True, + attachment_filename=u'Ñandú/pingüino.txt' + ) + value, options = parse_options_header( + rv.headers['Content-Disposition'] + ) + rv.close() + + assert value == 'attachment' + assert sorted(options.keys()) == ('filename', 'filename*') + assert options['filename'] == 'Nandu/pinguino.txt' + assert options['filename*'] == 'UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt' def test_static_file(self): app = flask.Flask(__name__) From f790ab7177c9959b560d238f4be9c124d635e70a Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 8 Apr 2017 10:33:06 -0700 Subject: [PATCH 142/399] need to test against raw header parsing prefers the last value parsed for the option --- tests/test_helpers.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 1aeff065..d93b443e 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -541,7 +541,7 @@ class TestSendfile(object): parse_options_header(rv.headers['Content-Disposition']) assert value == 'attachment' assert options['filename'] == 'index.html' - assert 'filename*' not in options + assert 'filename*' not in rv.headers['Content-Disposition'] rv.close() with app.test_request_context(): @@ -565,20 +565,15 @@ class TestSendfile(object): app = flask.Flask(__name__) with app.test_request_context(): - rv = flask.send_file( - 'static/index.html', as_attachment=True, - attachment_filename=u'Ñandú/pingüino.txt' - ) - value, options = parse_options_header( - rv.headers['Content-Disposition'] - ) + rv = flask.send_file('static/index.html', as_attachment=True, attachment_filename=u'Ñandú/pingüino.txt') + content_disposition = set(rv.headers['Content-Disposition'].split('; ')) + assert content_disposition == set(( + 'attachment', + 'filename="Nandu/pinguino.txt"', + "filename*=UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt" + )) rv.close() - assert value == 'attachment' - assert sorted(options.keys()) == ('filename', 'filename*') - assert options['filename'] == 'Nandu/pinguino.txt' - assert options['filename*'] == 'UTF-8''%C3%91and%C3%BA%EF%BC%8Fping%C3%BCino.txt' - def test_static_file(self): app = flask.Flask(__name__) # default cache timeout is 12 hours From aafb80c527eb5d6d361d7ba08258376d13679820 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 8 Apr 2017 11:08:08 -0700 Subject: [PATCH 143/399] add changelog for #2223 --- CHANGES | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES b/CHANGES index 440cb260..1f687409 100644 --- a/CHANGES +++ b/CHANGES @@ -18,7 +18,9 @@ Major release, unreleased and ``static_host``. This enables ``host_matching`` to be set properly by the time the constructor adds the static route, and enables the static route to be properly associated with the required host. (``#1559``) +- ``send_file`` supports Unicode in ``attachment_filename``. (`#2223`_) +.. _#2223: https://github.com/pallets/flask/pull/2223 Version 0.12.1 -------------- From e13eaeeaf2a3502815bcf04a86227dae87ad9128 Mon Sep 17 00:00:00 2001 From: ka7 Date: Tue, 11 Apr 2017 21:44:32 +0200 Subject: [PATCH 144/399] Fix typo in docs (#2237) --- docs/patterns/packages.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/packages.rst b/docs/patterns/packages.rst index 1bb84f8c..cc149839 100644 --- a/docs/patterns/packages.rst +++ b/docs/patterns/packages.rst @@ -65,7 +65,7 @@ that tells Flask where to find the application instance:: export FLASK_APP=yourapplication If you are outside of the project directory make sure to provide the exact -path to your application directory. Similiarly you can turn on "debug +path to your application directory. Similarly you can turn on "debug mode" with this environment variable:: export FLASK_DEBUG=true From 09b49104f39f111b58d60a819551cca4bde305cc Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 12 Apr 2017 09:18:07 -0700 Subject: [PATCH 145/399] filename can be latin-1, not just ascii only normalize basic name when utf-8 header is needed ref #2223 --- flask/helpers.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index 420467ad..bfdcf224 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -484,7 +484,7 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, .. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4 - :param filename_or_fp: the filename of the file to send in `latin-1`. + :param filename_or_fp: the filename of the file to send. This is relative to the :attr:`~Flask.root_path` if a relative path is specified. Alternatively a file object might be provided in @@ -541,15 +541,12 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, raise TypeError('filename unavailable, required for ' 'sending as attachment') - normalized = unicodedata.normalize( - 'NFKD', text_type(attachment_filename) - ) - try: - normalized.encode('ascii') + attachment_filename = attachment_filename.encode('latin-1') except UnicodeEncodeError: filenames = { - 'filename': normalized.encode('ascii', 'ignore'), + 'filename': unicodedata.normalize( + 'NFKD', attachment_filename).encode('latin-1', 'ignore'), 'filename*': "UTF-8''%s" % url_quote(attachment_filename), } else: From bf6910a639fffc6896c5aefe362edaf6f6b704fe Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 13 Apr 2017 14:55:56 -0700 Subject: [PATCH 146/399] get mtime in utc --- tests/test_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index d93b443e..d3906860 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -517,7 +517,7 @@ class TestSendfile(object): assert rv.status_code == 416 rv.close() - last_modified = datetime.datetime.fromtimestamp(os.path.getmtime( + last_modified = datetime.datetime.utcfromtimestamp(os.path.getmtime( os.path.join(app.root_path, 'static/index.html'))).replace( microsecond=0) From 1caa9de6286c5575accb05b942531080142242d3 Mon Sep 17 00:00:00 2001 From: accraze Date: Mon, 19 Dec 2016 08:03:37 -0800 Subject: [PATCH 147/399] Added missing testing config fixes #1302 --- docs/testing.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/testing.rst b/docs/testing.rst index 0737936e..edaa597c 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -98,8 +98,10 @@ test method to our class, like this:: def setUp(self): 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) From 03857cc48a1aff7af4c2bbc18a5740d1dc903c0b Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 13 Apr 2017 16:32:44 -0700 Subject: [PATCH 148/399] use app.testing property instead of config --- docs/testing.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index edaa597c..6fd7b504 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -41,7 +41,7 @@ In order to test the application, we add a second module def setUp(self): self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.config['TESTING'] = True + flaskr.app.testing = True self.app = flaskr.app.test_client() with flaskr.app.app_context(): flaskr.init_db() @@ -98,7 +98,7 @@ test method to our class, like this:: def setUp(self): self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.config['TESTING'] = True + flaskr.app.testing = True self.app = flaskr.app.test_client() with flaskr.app.app_context(): flaskr.init_db() @@ -210,7 +210,7 @@ temporarily. With this you can access the :class:`~flask.request`, functions. Here is a full example that demonstrates this approach:: import flask - + app = flask.Flask(__name__) with app.test_request_context('/?name=Peter'): From cfd3e50ab6447a69019fc008c260f8c078c4ac7d Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 13 Apr 2017 16:32:44 -0700 Subject: [PATCH 149/399] use app.testing property instead of config --- docs/testing.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 0737936e..6fd7b504 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -41,7 +41,7 @@ In order to test the application, we add a second module def setUp(self): self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.config['TESTING'] = True + flaskr.app.testing = True self.app = flaskr.app.test_client() with flaskr.app.app_context(): flaskr.init_db() @@ -98,8 +98,10 @@ test method to our class, like this:: def setUp(self): self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() + flaskr.app.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) @@ -208,7 +210,7 @@ temporarily. With this you can access the :class:`~flask.request`, functions. Here is a full example that demonstrates this approach:: import flask - + app = flask.Flask(__name__) with app.test_request_context('/?name=Peter'): From 4ff84d537aa386fde36182ed797a79e3b582be75 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 13 Apr 2017 14:55:56 -0700 Subject: [PATCH 150/399] get mtime in utc --- tests/test_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 3e2ea8cd..b1241ce9 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -515,7 +515,7 @@ class TestSendfile(object): assert rv.status_code == 416 rv.close() - last_modified = datetime.datetime.fromtimestamp(os.path.getmtime( + last_modified = datetime.datetime.utcfromtimestamp(os.path.getmtime( os.path.join(app.root_path, 'static/index.html'))).replace( microsecond=0) From 7481844c98e4536ae01764aa08eec681c493ef2e Mon Sep 17 00:00:00 2001 From: Sobolev Nikita Date: Wed, 19 Apr 2017 08:46:33 +0300 Subject: [PATCH 151/399] Fix typo in app.py (#2248) --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index 87621aee..c26387f0 100644 --- a/flask/app.py +++ b/flask/app.py @@ -544,7 +544,7 @@ class Flask(_PackageBoundObject): self._before_request_lock = Lock() # Add a static route using the provided static_url_path, static_host, - # and static_folder iff there is a configured static_folder. + # and static_folder if there is a configured static_folder. # Note we do this without checking if static_folder exists. # For one, it might be created while the server is running (e.g. during # development). Also, Google App Engine stores static files somewhere From 19fbe3a18f36b02f22f8f545e015f60343c844ac Mon Sep 17 00:00:00 2001 From: rocambolesque Date: Fri, 9 Sep 2016 12:11:18 +0200 Subject: [PATCH 152/399] Add scheme to url_build error handler parameters --- CHANGES | 3 +++ flask/helpers.py | 1 + 2 files changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 1f687409..d8f2577a 100644 --- a/CHANGES +++ b/CHANGES @@ -19,7 +19,10 @@ Major release, unreleased time the constructor adds the static route, and enables the static route to be properly associated with the required host. (``#1559``) - ``send_file`` supports Unicode in ``attachment_filename``. (`#2223`_) +- Pass ``_scheme`` argument from ``url_for`` to ``handle_build_error``. + (`#2017`_) +.. _#2017: https://github.com/pallets/flask/pull/2017 .. _#2223: https://github.com/pallets/flask/pull/2223 Version 0.12.1 diff --git a/flask/helpers.py b/flask/helpers.py index bfdcf224..828f5840 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -331,6 +331,7 @@ def url_for(endpoint, **values): values['_external'] = external values['_anchor'] = anchor values['_method'] = method + values['_scheme'] = scheme return appctx.app.handle_url_build_error(error, endpoint, values) if anchor is not None: From e50767cfca081681f527c54d3f6652d11e95e758 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 20 Apr 2017 08:52:37 -0700 Subject: [PATCH 153/399] add test for build error special values --- tests/test_basic.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_basic.py b/tests/test_basic.py index 76efd6e2..abce3ad4 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1131,6 +1131,23 @@ def test_build_error_handler_reraise(): pytest.raises(BuildError, flask.url_for, 'not.existing') +def test_url_for_passes_special_values_to_build_error_handler(): + app = flask.Flask(__name__) + + @app.url_build_error_handlers.append + def handler(error, endpoint, values): + assert values == { + '_external': False, + '_anchor': None, + '_method': None, + '_scheme': None, + } + return 'handled' + + with app.test_request_context(): + flask.url_for('/') + + def test_custom_converters(): from werkzeug.routing import BaseConverter From 97e2cd0a5a5f2b663d7a82edbaede2fe2cfb0679 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 21 Apr 2017 07:16:09 -0700 Subject: [PATCH 154/399] update changelog move test next to existing test, rename reword / reflow param doc --- CHANGES | 7 ++-- flask/app.py | 11 +++--- tests/test_basic.py | 91 ++++++++++++++++++++++++--------------------- 3 files changed, 58 insertions(+), 51 deletions(-) diff --git a/CHANGES b/CHANGES index 34f7f13e..dba0112e 100644 --- a/CHANGES +++ b/CHANGES @@ -21,7 +21,11 @@ Major release, unreleased - ``send_file`` supports Unicode in ``attachment_filename``. (`#2223`_) - Pass ``_scheme`` argument from ``url_for`` to ``handle_build_error``. (`#2017`_) +- Add support for ``provide_automatic_options`` in ``add_url_rule`` to disable + adding OPTIONS method when the ``view_func`` argument is not a class. + (`#1489`_). +.. _#1489: https://github.com/pallets/flask/pull/1489 .. _#2017: https://github.com/pallets/flask/pull/2017 .. _#2223: https://github.com/pallets/flask/pull/2223 @@ -146,9 +150,6 @@ Released on May 29th 2016, codename Absinthe. - ``flask.g`` now has ``pop()`` and ``setdefault`` methods. - Turn on autoescape for ``flask.templating.render_template_string`` by default (pull request ``#1515``). -- Added support for `provide_automatic_options` in :meth:`add_url_rule` to - turn off automatic OPTIONS when the `view_func` argument is not a class - (pull request ``#1489``). - ``flask.ext`` is now deprecated (pull request ``#1484``). - ``send_from_directory`` now raises BadRequest if the filename is invalid on the server OS (pull request ``#1763``). diff --git a/flask/app.py b/flask/app.py index 32f5086c..a054d23c 100644 --- a/flask/app.py +++ b/flask/app.py @@ -980,8 +980,7 @@ class Flask(_PackageBoundObject): return iter(self._blueprint_order) @setupmethod - def add_url_rule(self, rule, endpoint=None, view_func=None, - provide_automatic_options=None, **options): + def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options): """Connects a URL rule. Works exactly like the :meth:`route` decorator. If a view_func is provided it will be registered with the endpoint. @@ -1021,10 +1020,10 @@ class Flask(_PackageBoundObject): endpoint :param view_func: the function to call when serving a request to the provided endpoint - :param provide_automatic_options: controls whether ``OPTIONS`` should - be provided automatically. If this - is not set, will check attributes on - the view or list of methods. + :param provide_automatic_options: controls whether the ``OPTIONS`` + method should be added automatically. This can also be controlled + by setting the ``view_func.provide_automatic_options = False`` + before adding the rule. :param options: the options to be forwarded to the underlying :class:`~werkzeug.routing.Rule` object. A change to Werkzeug is handling of method options. methods diff --git a/tests/test_basic.py b/tests/test_basic.py index 3fe69588..677b4be8 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -50,7 +50,7 @@ def test_options_on_multiple_rules(): assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] -def test_options_handling_disabled(): +def test_provide_automatic_options_attr(): app = flask.Flask(__name__) def index(): @@ -70,6 +70,54 @@ def test_options_handling_disabled(): assert sorted(rv.allow) == ['OPTIONS'] +def test_provide_automatic_options_kwarg(): + app = flask.Flask(__name__) + + def index(): + return flask.request.method + + def more(): + return flask.request.method + + app.add_url_rule('/', view_func=index, provide_automatic_options=False) + app.add_url_rule( + '/more', view_func=more, methods=['GET', 'POST'], + provide_automatic_options=False + ) + + c = app.test_client() + assert c.get('/').data == b'GET' + + rv = c.post('/') + assert rv.status_code == 405 + assert sorted(rv.allow) == ['GET', 'HEAD'] + + # Older versions of Werkzeug.test.Client don't have an options method + if hasattr(c, 'options'): + rv = c.options('/') + else: + rv = c.open('/', method='OPTIONS') + + assert rv.status_code == 405 + + rv = c.head('/') + assert rv.status_code == 200 + assert not rv.data # head truncates + assert c.post('/more').data == b'POST' + assert c.get('/more').data == b'GET' + + rv = c.delete('/more') + assert rv.status_code == 405 + assert sorted(rv.allow) == ['GET', 'HEAD', 'POST'] + + if hasattr(c, 'options'): + rv = c.options('/more') + else: + rv = c.open('/more', method='OPTIONS') + + assert rv.status_code == 405 + + def test_request_dispatching(): app = flask.Flask(__name__) @@ -1751,44 +1799,3 @@ def test_run_from_config(monkeypatch, host, port, expect_host, expect_port): app = flask.Flask(__name__) app.config['SERVER_NAME'] = 'pocoo.org:8080' app.run(host, port) - - -def test_disable_automatic_options(): - # Issue 1488: Add support for a kwarg to add_url_rule to disable the auto OPTIONS response - app = flask.Flask(__name__) - - def index(): - return flask.request.method - - def more(): - return flask.request.method - - app.add_url_rule('/', 'index', index, provide_automatic_options=False) - app.add_url_rule('/more', 'more', more, methods=['GET', 'POST'], provide_automatic_options=False) - - c = app.test_client() - assert c.get('/').data == b'GET' - rv = c.post('/') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD'] - # Older versions of Werkzeug.test.Client don't have an options method - if hasattr(c, 'options'): - rv = c.options('/') - else: - rv = c.open('/', method='OPTIONS') - assert rv.status_code == 405 - - rv = c.head('/') - assert rv.status_code == 200 - assert not rv.data # head truncates - assert c.post('/more').data == b'POST' - assert c.get('/more').data == b'GET' - rv = c.delete('/more') - assert rv.status_code == 405 - assert sorted(rv.allow) == ['GET', 'HEAD', 'POST'] - # Older versions of Werkzeug.test.Client don't have an options method - if hasattr(c, 'options'): - rv = c.options('/more') - else: - rv = c.open('/more', method='OPTIONS') - assert rv.status_code == 405 From 648344d4e8878e4aafcc5413f984b21b86173246 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 21 Apr 2017 10:32:00 -0700 Subject: [PATCH 155/399] use mro to collect methods ignore methods attr unless explicitly set add changelog --- CHANGES | 2 ++ flask/views.py | 44 ++++++++++++++++++++++---------------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/CHANGES b/CHANGES index dba0112e..4fd46d71 100644 --- a/CHANGES +++ b/CHANGES @@ -24,8 +24,10 @@ Major release, unreleased - Add support for ``provide_automatic_options`` in ``add_url_rule`` to disable adding OPTIONS method when the ``view_func`` argument is not a class. (`#1489`_). +- ``MethodView`` can inherit method handlers from base classes. (`#1936`_) .. _#1489: https://github.com/pallets/flask/pull/1489 +.. _#1936: https://github.com/pallets/flask/pull/1936 .. _#2017: https://github.com/pallets/flask/pull/2017 .. _#2223: https://github.com/pallets/flask/pull/2223 diff --git a/flask/views.py b/flask/views.py index 757c2a4d..848ccb0b 100644 --- a/flask/views.py +++ b/flask/views.py @@ -102,38 +102,35 @@ class View(object): return view -def get_methods(cls): - return getattr(cls, 'methods', []) or [] - - class MethodViewType(type): + """Metaclass for :class:`MethodView` that determines what methods the view + defines. + """ + + def __init__(cls, name, bases, d): + super(MethodViewType, cls).__init__(name, bases, d) - def __new__(cls, name, bases, d): - rv = type.__new__(cls, name, bases, d) if 'methods' not in d: - methods = set(m for b in bases for m in get_methods(b)) - for key in d: - if key in http_method_funcs: + methods = set() + + for key in http_method_funcs: + if hasattr(cls, key): methods.add(key.upper()) - # 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 base class or another subclass of a base method view - # that does not introduce new methods). + + # 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 base class + # or another subclass of a base method view that does not introduce + # new methods. if methods: - rv.methods = sorted(methods) - return rv + cls.methods = methods class MethodView(with_metaclass(MethodViewType, View)): - """Like a regular class-based view but that dispatches requests to - particular methods. For instance if you implement a method called - :meth:`get` it means it will respond to ``'GET'`` requests and - the :meth:`dispatch_request` implementation will automatically - forward your request to that. Also :attr:`options` is set for you - automatically:: + """A class-based view that dispatches request methods to the corresponding + class methods. For example, if you implement a ``get`` method, it will be + used to handle ``GET`` requests. :: class CounterAPI(MethodView): - def get(self): return session.get('counter', 0) @@ -143,11 +140,14 @@ class MethodView(with_metaclass(MethodViewType, View)): app.add_url_rule('/counter', view_func=CounterAPI.as_view('counter')) """ + 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 meth is None and request.method == 'HEAD': meth = getattr(self, 'get', None) + assert meth is not None, 'Unimplemented method %r' % request.method return meth(*args, **kwargs) From 13754b6d117eb6847cca45475412fdd05025f1b8 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 22 Apr 2017 13:39:54 -0700 Subject: [PATCH 156/399] ensure error while opening session pops context errors will be handled by the app error handlers closes #1538, closes #1528 --- CHANGES | 3 +++ flask/app.py | 2 +- tests/test_reqctx.py | 25 +++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 4fd46d71..d7f15d66 100644 --- a/CHANGES +++ b/CHANGES @@ -25,11 +25,14 @@ Major release, unreleased adding OPTIONS method when the ``view_func`` argument is not a class. (`#1489`_). - ``MethodView`` can inherit method handlers from base classes. (`#1936`_) +- Errors caused while opening the session at the beginning of the request are + handled by the app's error handlers. (`#2254`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1936: https://github.com/pallets/flask/pull/1936 .. _#2017: https://github.com/pallets/flask/pull/2017 .. _#2223: https://github.com/pallets/flask/pull/2223 +.. _#2254: https://github.com/pallets/flask/pull/2254 Version 0.12.1 -------------- diff --git a/flask/app.py b/flask/app.py index a054d23c..1943cfcf 100644 --- a/flask/app.py +++ b/flask/app.py @@ -2002,10 +2002,10 @@ class Flask(_PackageBoundObject): exception context to start the response """ ctx = self.request_context(environ) - ctx.push() error = None try: try: + ctx.push() response = self.full_dispatch_request() except Exception as e: error = e diff --git a/tests/test_reqctx.py b/tests/test_reqctx.py index 4b2b1f87..48823fb2 100644 --- a/tests/test_reqctx.py +++ b/tests/test_reqctx.py @@ -12,6 +12,7 @@ import pytest import flask +from flask.sessions import SessionInterface try: from greenlet import greenlet @@ -193,3 +194,27 @@ def test_greenlet_context_copying_api(): result = greenlets[0].run() assert result == 42 + + +def test_session_error_pops_context(): + class SessionError(Exception): + pass + + class FailingSessionInterface(SessionInterface): + def open_session(self, app, request): + raise SessionError() + + class CustomFlask(flask.Flask): + session_interface = FailingSessionInterface() + + app = CustomFlask(__name__) + + @app.route('/') + def index(): + # shouldn't get here + assert False + + response = app.test_client().get('/') + assert response.status_code == 500 + assert not flask.request + assert not flask.current_app From 46f83665ef74880bbc4a1c1f17ccededa7aaa939 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 24 Apr 2017 10:09:50 -0700 Subject: [PATCH 157/399] clean up blueprint json support add changelog for #1898 --- CHANGES | 3 +++ flask/blueprints.py | 4 ++-- flask/json.py | 24 ++++++++++++++---------- tests/test_helpers.py | 16 +++++++++++----- 4 files changed, 30 insertions(+), 17 deletions(-) diff --git a/CHANGES b/CHANGES index d7f15d66..ae592ba8 100644 --- a/CHANGES +++ b/CHANGES @@ -27,8 +27,11 @@ Major release, unreleased - ``MethodView`` can inherit method handlers from base classes. (`#1936`_) - Errors caused while opening the session at the beginning of the request are handled by the app's error handlers. (`#2254`_) +- Blueprints gained ``json_encoder`` and ``json_decoder`` attributes to + override the app's encoder and decoder. (`#1898`_) .. _#1489: https://github.com/pallets/flask/pull/1489 +.. _#1898: https://github.com/pallets/flask/pull/1898 .. _#1936: https://github.com/pallets/flask/pull/1936 .. _#2017: https://github.com/pallets/flask/pull/2017 .. _#2223: https://github.com/pallets/flask/pull/2223 diff --git a/flask/blueprints.py b/flask/blueprints.py index 62675204..57d77512 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -90,10 +90,10 @@ class Blueprint(_PackageBoundObject): _got_registered_once = False #: Blueprint local JSON decoder class to use. - # Set to None to use the :class:`~flask.app.Flask.json_encoder`. + #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_encoder`. json_encoder = None #: Blueprint local JSON decoder class to use. - # Set to None to use the :class:`~flask.app.Flask.json_decoder`. + #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`. json_decoder = None def __init__(self, name, import_name, static_folder=None, diff --git a/flask/json.py b/flask/json.py index 77b5fce1..bf8a8843 100644 --- a/flask/json.py +++ b/flask/json.py @@ -92,13 +92,16 @@ class JSONDecoder(_json.JSONDecoder): def _dump_arg_defaults(kwargs): """Inject default arguments for dump functions.""" if current_app: - bp = current_app.blueprints.get(request.blueprint, - None) if has_request_context() else None - kwargs.setdefault('cls', - bp.json_encoder if bp and bp.json_encoder - else current_app.json_encoder) + bp = current_app.blueprints.get(request.blueprint) if request else None + kwargs.setdefault( + 'cls', + bp.json_encoder if bp and bp.json_encoder + else current_app.json_encoder + ) + if not current_app.config['JSON_AS_ASCII']: kwargs.setdefault('ensure_ascii', False) + kwargs.setdefault('sort_keys', current_app.config['JSON_SORT_KEYS']) else: kwargs.setdefault('sort_keys', True) @@ -108,11 +111,12 @@ def _dump_arg_defaults(kwargs): def _load_arg_defaults(kwargs): """Inject default arguments for load functions.""" if current_app: - bp = current_app.blueprints.get(request.blueprint, - None) if has_request_context() else None - kwargs.setdefault('cls', - bp.json_decoder if bp and bp.json_decoder - else current_app.json_decoder) + bp = current_app.blueprints.get(request.blueprint) if request else None + kwargs.setdefault( + 'cls', + bp.json_decoder if bp and bp.json_decoder + else current_app.json_decoder + ) else: kwargs.setdefault('cls', JSONDecoder) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index c811e1b7..325713c0 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -271,30 +271,36 @@ class TestJSON(object): class X(object): def __init__(self, val): self.val = val + class MyEncoder(flask.json.JSONEncoder): def default(self, o): if isinstance(o, X): return '<%d>' % o.val + return flask.json.JSONEncoder.default(self, o) + class MyDecoder(flask.json.JSONDecoder): def __init__(self, *args, **kwargs): kwargs.setdefault('object_hook', self.object_hook) flask.json.JSONDecoder.__init__(self, *args, **kwargs) + def object_hook(self, obj): if len(obj) == 1 and '_foo' in obj: return X(obj['_foo']) + return obj - blue = flask.Blueprint('blue', __name__) - blue.json_encoder = MyEncoder - blue.json_decoder = MyDecoder - @blue.route('/bp', methods=['POST']) + bp = flask.Blueprint('bp', __name__) + bp.json_encoder = MyEncoder + bp.json_decoder = MyDecoder + + @bp.route('/bp', methods=['POST']) def index(): return flask.json.dumps(flask.request.get_json()['x']) app = flask.Flask(__name__) app.testing = True - app.register_blueprint(blue) + app.register_blueprint(bp) c = app.test_client() rv = c.post('/bp', data=flask.json.dumps({ From 697f7b9365304c45216e6c8dd307d931ea1506d0 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 24 Apr 2017 14:11:49 -0700 Subject: [PATCH 158/399] refactor make_response to be easier to follow * be explicit about how tuples are unpacked * allow bytes for status value * allow Headers for headers value * use TypeError instead of ValueError * errors are more descriptive * document that view must not return None * update documentation about return values * test more response types * test error messages closes #1676 --- CHANGES | 4 ++ flask/app.py | 170 ++++++++++++++++++++++++++++---------------- tests/test_basic.py | 143 +++++++++++++++++++++++++------------ 3 files changed, 207 insertions(+), 110 deletions(-) diff --git a/CHANGES b/CHANGES index ae592ba8..11ac6430 100644 --- a/CHANGES +++ b/CHANGES @@ -29,6 +29,9 @@ Major release, unreleased handled by the app's error handlers. (`#2254`_) - Blueprints gained ``json_encoder`` and ``json_decoder`` attributes to override the app's encoder and decoder. (`#1898`_) +- ``Flask.make_response`` raises ``TypeError`` instead of ``ValueError`` for + bad response types. The error messages have been improved to describe why the + type is invalid. (`#2256`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1898: https://github.com/pallets/flask/pull/1898 @@ -36,6 +39,7 @@ Major release, unreleased .. _#2017: https://github.com/pallets/flask/pull/2017 .. _#2223: https://github.com/pallets/flask/pull/2223 .. _#2254: https://github.com/pallets/flask/pull/2254 +.. _#2256: https://github.com/pallets/flask/pull/2256 Version 0.12.1 -------------- diff --git a/flask/app.py b/flask/app.py index 1943cfcf..a938344b 100644 --- a/flask/app.py +++ b/flask/app.py @@ -10,30 +10,30 @@ """ import os import sys -from threading import Lock from datetime import timedelta -from itertools import chain from functools import update_wrapper +from itertools import chain +from threading import Lock -from werkzeug.datastructures import ImmutableDict -from werkzeug.routing import Map, Rule, RequestRedirect, BuildError -from werkzeug.exceptions import HTTPException, InternalServerError, \ - MethodNotAllowed, BadRequest, default_exceptions - -from .helpers import _PackageBoundObject, url_for, get_flashed_messages, \ - locked_cached_property, _endpoint_from_view_func, find_package, \ - get_debug_flag -from . import json, cli -from .wrappers import Request, Response -from .config import ConfigAttribute, Config -from .ctx import RequestContext, AppContext, _AppCtxGlobals -from .globals import _request_ctx_stack, request, session, g +from werkzeug.datastructures import ImmutableDict, Headers +from werkzeug.exceptions import BadRequest, HTTPException, \ + InternalServerError, MethodNotAllowed, default_exceptions +from werkzeug.routing import BuildError, Map, RequestRedirect, Rule + +from . import cli, json +from ._compat import integer_types, reraise, string_types, text_type +from .config import Config, ConfigAttribute +from .ctx import AppContext, RequestContext, _AppCtxGlobals +from .globals import _request_ctx_stack, g, request, session +from .helpers import _PackageBoundObject, \ + _endpoint_from_view_func, find_package, get_debug_flag, \ + get_flashed_messages, locked_cached_property, url_for from .sessions import SecureCookieSessionInterface +from .signals import appcontext_tearing_down, got_request_exception, \ + request_finished, request_started, request_tearing_down 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 ._compat import reraise, string_types, text_type, integer_types + _default_template_ctx_processor +from .wrappers import Request, Response # a lock used for logger initialization _logger_lock = Lock() @@ -1715,62 +1715,106 @@ class Flask(_PackageBoundObject): return False def make_response(self, rv): - """Converts the return value from a view function to a real - response object that is an instance of :attr:`response_class`. - - The following types are allowed for `rv`: - - .. tabularcolumns:: |p{3.5cm}|p{9.5cm}| - - ======================= =========================================== - :attr:`response_class` the object is returned unchanged - :class:`str` a response object is created with the - string as body - :class:`unicode` a response object is created with the - string encoded to utf-8 as body - 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)`` 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. - ======================= =========================================== - - :param rv: the return value from the view function + """Convert the return value from a view function to an instance of + :attr:`response_class`. + + :param rv: the return value from the view function. The view function + must return a response. Returning ``None``, or the view ending + without returning, is not allowed. The following types are allowed + for ``view_rv``: + + ``str`` (``unicode`` in Python 2) + A response object is created with the string encoded to UTF-8 + as the body. + + ``bytes`` (``str`` in Python 2) + A response object is created with the bytes as the body. + + ``tuple`` + Either ``(body, status, headers)``, ``(body, status)``, or + ``(body, headers)``, where ``body`` is any of the other types + allowed here, ``status`` is a string or an integer, and + ``headers`` is a dictionary or a list of ``(key, value)`` + tuples. If ``body`` is a :attr:`response_class` instance, + ``status`` overwrites the exiting value and ``headers`` are + extended. + + :attr:`response_class` + The object is returned unchanged. + + other :class:`~werkzeug.wrappers.Response` class + The object is coerced to :attr:`response_class`. + + :func:`callable` + The function is called as a WSGI application. The result is + used to create a response object. .. versionchanged:: 0.9 Previously a tuple was interpreted as the arguments for the response object. """ - status_or_headers = headers = None - if isinstance(rv, tuple): - rv, status_or_headers, headers = rv + (None,) * (3 - len(rv)) - if rv is None: - raise ValueError('View function did not return a response') + status = headers = None + + # unpack tuple returns + if isinstance(rv, (tuple, list)): + len_rv = len(rv) - if isinstance(status_or_headers, (dict, list)): - headers, status_or_headers = status_or_headers, None + # a 3-tuple is unpacked directly + if len_rv == 3: + rv, status, headers = rv + # decide if a 2-tuple has status or headers + elif len_rv == 2: + if isinstance(rv[1], (Headers, dict, tuple, list)): + rv, headers = rv + else: + rv, status = rv + # other sized tuples are not allowed + else: + raise TypeError( + 'The view function did not return a valid response tuple.' + ' The tuple must have the form (body, status, headers),' + ' (body, status), or (body, headers).' + ) + # the body must not be None + if rv is None: + raise TypeError( + 'The view function did not return a valid response. The' + ' function either returned None or ended without a return' + ' statement.' + ) + + # make sure the body is an instance of the response class 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_or_headers) - headers = status_or_headers = None + # let the response class set the status and headers instead of + # waiting to do it manually, so that the class can handle any + # special logic + rv = self.response_class(rv, status=status, headers=headers) + status = headers = None else: - rv = self.response_class.force_type(rv, request.environ) - - if status_or_headers is not None: - if isinstance(status_or_headers, string_types): - rv.status = status_or_headers + # evaluate a WSGI callable, or coerce a different response + # class to the correct type + try: + rv = self.response_class.force_type(rv, request.environ) + except TypeError as e: + new_error = TypeError( + '{e}\nThe view function did not return a valid' + ' response. The return type must be a string, tuple,' + ' Response instance, or WSGI callable, but it was a' + ' {rv.__class__.__name__}.'.format(e=e, rv=rv) + ) + reraise(TypeError, new_error, sys.exc_info()[2]) + + # prefer the status if it was provided + if status is not None: + if isinstance(status, (text_type, bytes, bytearray)): + rv.status = status else: - rv.status_code = status_or_headers + rv.status_code = status + + # extend existing headers with provided headers if headers: rv.headers.extend(headers) diff --git a/tests/test_basic.py b/tests/test_basic.py index 677b4be8..163b83cf 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -975,64 +975,129 @@ def test_enctype_debug_helper(): assert 'This was submitted: "index.txt"' in str(e.value) -def test_response_creation(): +def test_response_types(): app = flask.Flask(__name__) + app.testing = True - @app.route('/unicode') - def from_unicode(): + @app.route('/text') + def from_text(): return u'Hällo Wörld' - @app.route('/string') - def from_string(): + @app.route('/bytes') + def from_bytes(): return u'Hällo Wörld'.encode('utf-8') - @app.route('/args') - def from_tuple(): + @app.route('/full_tuple') + def from_full_tuple(): return 'Meh', 400, { 'X-Foo': 'Testing', 'Content-Type': 'text/plain; charset=utf-8' } - @app.route('/two_args') - def from_two_args_tuple(): + @app.route('/text_headers') + def from_text_headers(): return 'Hello', { 'X-Foo': 'Test', 'Content-Type': 'text/plain; charset=utf-8' } - @app.route('/args_status') - def from_status_tuple(): + @app.route('/text_status') + def from_text_status(): return 'Hi, status!', 400 - @app.route('/args_header') - def from_response_instance_status_tuple(): - return flask.Response('Hello world', 404), { + @app.route('/response_headers') + def from_response_headers(): + return flask.Response('Hello world', 404, {'X-Foo': 'Baz'}), { "X-Foo": "Bar", "X-Bar": "Foo" } + @app.route('/response_status') + def from_response_status(): + return app.response_class('Hello world', 400), 500 + + @app.route('/wsgi') + def from_wsgi(): + return NotFound() + c = app.test_client() - assert c.get('/unicode').data == u'Hällo Wörld'.encode('utf-8') - assert c.get('/string').data == u'Hällo Wörld'.encode('utf-8') - rv = c.get('/args') + + assert c.get('/text').data == u'Hällo Wörld'.encode('utf-8') + assert c.get('/bytes').data == u'Hällo Wörld'.encode('utf-8') + + rv = c.get('/full_tuple') assert rv.data == b'Meh' assert rv.headers['X-Foo'] == 'Testing' assert rv.status_code == 400 assert rv.mimetype == 'text/plain' - rv2 = c.get('/two_args') - assert rv2.data == b'Hello' - assert rv2.headers['X-Foo'] == 'Test' - assert rv2.status_code == 200 - assert rv2.mimetype == 'text/plain' - rv3 = c.get('/args_status') - assert rv3.data == b'Hi, status!' - assert rv3.status_code == 400 - assert rv3.mimetype == 'text/html' - rv4 = c.get('/args_header') - assert rv4.data == b'Hello world' - assert rv4.headers['X-Foo'] == 'Bar' - assert rv4.headers['X-Bar'] == 'Foo' - assert rv4.status_code == 404 + + rv = c.get('/text_headers') + assert rv.data == b'Hello' + assert rv.headers['X-Foo'] == 'Test' + assert rv.status_code == 200 + assert rv.mimetype == 'text/plain' + + rv = c.get('/text_status') + assert rv.data == b'Hi, status!' + assert rv.status_code == 400 + assert rv.mimetype == 'text/html' + + rv = c.get('/response_headers') + assert rv.data == b'Hello world' + assert rv.headers.getlist('X-Foo') == ['Baz', 'Bar'] + assert rv.headers['X-Bar'] == 'Foo' + assert rv.status_code == 404 + + rv = c.get('/response_status') + assert rv.data == b'Hello world' + assert rv.status_code == 500 + + rv = c.get('/wsgi') + assert b'Not Found' in rv.data + assert rv.status_code == 404 + + +def test_response_type_errors(): + app = flask.Flask(__name__) + app.testing = True + + @app.route('/none') + def from_none(): + pass + + @app.route('/small_tuple') + def from_small_tuple(): + return 'Hello', + + @app.route('/large_tuple') + def from_large_tuple(): + return 'Hello', 234, {'X-Foo': 'Bar'}, '???' + + @app.route('/bad_type') + def from_bad_type(): + return True + + @app.route('/bad_wsgi') + def from_bad_wsgi(): + return lambda: None + + c = app.test_client() + + with pytest.raises(TypeError) as e: + c.get('/none') + assert 'returned None' in str(e) + + with pytest.raises(TypeError) as e: + c.get('/small_tuple') + assert 'tuple must have the form' in str(e) + + pytest.raises(TypeError, c.get, '/large_tuple') + + with pytest.raises(TypeError) as e: + c.get('/bad_type') + assert 'it was a bool' in str(e) + + pytest.raises(TypeError, c.get, '/bad_wsgi') def test_make_response(): @@ -1272,22 +1337,6 @@ def test_static_route_with_host_matching(): flask.Flask(__name__, host_matching=True, static_folder=None) -def test_none_response(): - app = flask.Flask(__name__) - app.testing = True - - @app.route('/') - def test(): - return None - try: - app.test_client().get('/') - except ValueError as e: - assert str(e) == 'View function did not return a response' - pass - else: - assert "Expected ValueError" - - def test_request_locals(): assert repr(flask.g) == '' assert not flask.g From 501f0431259a30569a5e62bcce68d102fc3ef993 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 25 Apr 2017 12:03:08 -0700 Subject: [PATCH 159/399] clean up preprocess_request docs [ci skip] --- flask/app.py | 70 ++++++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 32 deletions(-) diff --git a/flask/app.py b/flask/app.py index 84f55d2c..703cfef2 100644 --- a/flask/app.py +++ b/flask/app.py @@ -415,17 +415,16 @@ class Flask(_PackageBoundObject): #: .. versionadded:: 0.9 self.url_build_error_handlers = [] - #: A dictionary with lists of functions that should be called at the - #: beginning of the request. The key of the dictionary is the name of - #: the blueprint this function is active for, ``None`` for all requests. - #: This can for example be used to open database connections or - #: getting hold of the currently logged in user. To register a - #: function here, use the :meth:`before_request` decorator. + #: A dictionary with lists of functions that will be called at the + #: beginning of each request. The key of the dictionary is the name of + #: the blueprint this function is active for, or ``None`` for all + #: requests. To register a function, use the :meth:`before_request` + #: decorator. self.before_request_funcs = {} - #: A lists of functions that should be called at the beginning of the - #: first request to this instance. To register a function here, use - #: the :meth:`before_first_request` decorator. + #: A list of functions that will be called at the beginning of the + #: first request to this instance. To register a function, use the + #: :meth:`before_first_request` decorator. #: #: .. versionadded:: 0.8 self.before_first_request_funcs = [] @@ -457,12 +456,11 @@ class Flask(_PackageBoundObject): #: .. versionadded:: 0.9 self.teardown_appcontext_funcs = [] - #: A dictionary with lists of functions that can be used as URL - #: value processor functions. Whenever a URL is built these functions - #: are called to modify the dictionary of values in place. The key - #: ``None`` here is used for application wide - #: callbacks, otherwise the key is the name of the blueprint. - #: Each of these functions has the chance to modify the dictionary + #: A dictionary with lists of functions that are called before the + #: :attr:`before_request_funcs` functions. The key of the dictionary is + #: the name of the blueprint this function is active for, or ``None`` + #: for all requests. To register a function, use + #: :meth:`url_value_preprocessor`. #: #: .. versionadded:: 0.7 self.url_value_preprocessors = {} @@ -1314,11 +1312,13 @@ class Flask(_PackageBoundObject): @setupmethod def before_request(self, f): """Registers a function to run before each request. + + For example, this can be used to open a database connection, or to load + the logged in user from the session. - The function will be called without any arguments. - If the function returns a non-None value, it's handled as - if it was the return value from the view and further - request handling is stopped. + The function will be called without any arguments. If it returns a + non-None value, the value is handled as if it was the return value from + the view, and further request handling is stopped. """ self.before_request_funcs.setdefault(None, []).append(f) return f @@ -1437,9 +1437,17 @@ class Flask(_PackageBoundObject): @setupmethod def url_value_preprocessor(self, f): - """Registers a function as URL value preprocessor for all view - functions of the application. It's called before the view functions - are called and can modify the url values provided. + """Register a URL value preprocessor function for all view + functions in the application. These functions will be called before the + :meth:`before_request` functions. + + The function can modify the values captured from the matched url before + they are passed to the view. For example, this can be used to pop a + common language code value and place it in ``g`` rather than pass it to + every view. + + The function is passed the endpoint name and values dict. The return + value is ignored. """ self.url_value_preprocessors.setdefault(None, []).append(f) return f @@ -1877,16 +1885,14 @@ class Flask(_PackageBoundObject): raise error def preprocess_request(self): - """Called before the request dispatching. - - Triggers two set of hook functions that should be invoked prior to request dispatching: - :attr:`url_value_preprocessors` and :attr:`before_request_funcs` - (the latter are functions decorated with :meth:`before_request` decorator). - In both cases, the method triggers only the functions that are either global - or registered to the current blueprint. - - If any function in :attr:`before_request_funcs` returns a value, it's handled as if it was - the return value from the view function, and further request handling is stopped. + """Called before the request is dispatched. Calls + :attr:`url_value_preprocessors` registered with the app and the + current blueprint (if any). Then calls :attr:`before_request_funcs` + registered with the app and the blueprint. + + If any :meth:`before_request` handler returns a non-None value, the + value is handled as if it was the return value from the view, and + further request handling is stopped. """ bp = _request_ctx_stack.top.request.blueprint From 7ad79583b9558cc7806d56c534f9527e500734e9 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 25 Apr 2017 14:15:38 -0700 Subject: [PATCH 160/399] add sort by match order sort by endpoint by default combine sort flags sort methods ignore HEAD and OPTIONS methods by default rearrange columns use format to build row format string rework tests add changelog --- CHANGES | 3 ++ flask/cli.py | 74 +++++++++++++++---------- tests/test_apps/cliapp/routesapp.py | 18 ------- tests/test_cli.py | 84 ++++++++++++++++++----------- 4 files changed, 104 insertions(+), 75 deletions(-) delete mode 100644 tests/test_apps/cliapp/routesapp.py diff --git a/CHANGES b/CHANGES index 11ac6430..ddab541f 100644 --- a/CHANGES +++ b/CHANGES @@ -32,6 +32,8 @@ Major release, unreleased - ``Flask.make_response`` raises ``TypeError`` instead of ``ValueError`` for bad response types. The error messages have been improved to describe why the type is invalid. (`#2256`_) +- Add ``routes`` CLI command to output routes registered on the application. + (`#2259`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1898: https://github.com/pallets/flask/pull/1898 @@ -40,6 +42,7 @@ Major release, unreleased .. _#2223: https://github.com/pallets/flask/pull/2223 .. _#2254: https://github.com/pallets/flask/pull/2254 .. _#2256: https://github.com/pallets/flask/pull/2256 +.. _#2259: https://github.com/pallets/flask/pull/2259 Version 0.12.1 -------------- diff --git a/flask/cli.py b/flask/cli.py index 80aa1cd5..3d361be8 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -12,14 +12,17 @@ import os import sys import traceback -from threading import Lock, Thread from functools import update_wrapper +from operator import attrgetter +from threading import Lock, Thread import click +from . import __version__ from ._compat import iteritems, reraise +from .globals import current_app from .helpers import get_debug_flag -from . import __version__ + class NoAppException(click.UsageError): """Raised if an application cannot be found or loaded.""" @@ -485,34 +488,51 @@ def shell_command(): code.interact(banner=banner, local=ctx) -@click.command('routes', short_help='Show routes for the app.') -@click.option('-r', 'order_by', flag_value='rule', default=True, help='Order by route') -@click.option('-e', 'order_by', flag_value='endpoint', help='Order by endpoint') -@click.option('-m', 'order_by', flag_value='methods', help='Order by methods') +@click.command('routes', short_help='Show the routes for the app.') +@click.option( + '--sort', '-s', + type=click.Choice(('endpoint', 'methods', 'rule', 'match')), + default='endpoint', + help=( + 'Method to sort routes by. "match" is the order that Flask will match ' + 'routes when dispatching a request.' + ) +) +@click.option( + '--all-methods', + is_flag=True, + help="Show HEAD and OPTIONS methods." +) @with_appcontext -def routes_command(order_by): - """Show all routes with endpoints and methods.""" - from flask.globals import _app_ctx_stack - app = _app_ctx_stack.top.app - - order_key = lambda rule: getattr(rule, order_by) - sorted_rules = sorted(app.url_map.iter_rules(), key=order_key) - - max_rule = max(len(rule.rule) for rule in sorted_rules) - max_rule = max(max_rule, len('Route')) - max_ep = max(len(rule.endpoint) for rule in sorted_rules) - max_ep = max(max_ep, len('Endpoint')) - max_meth = max(len(', '.join(rule.methods)) for rule in sorted_rules) - max_meth = max(max_meth, len('Methods')) +def routes_command(sort, all_methods): + """Show all registered routes with endpoints and methods.""" + + rules = list(current_app.url_map.iter_rules()) + ignored_methods = set(() if all_methods else ('HEAD', 'OPTIONS')) + + if sort in ('endpoint', 'rule'): + rules = sorted(rules, key=attrgetter(sort)) + elif sort == 'methods': + rules = sorted(rules, key=lambda rule: sorted(rule.methods)) + + rule_methods = [ + ', '.join(sorted(rule.methods - ignored_methods)) for rule in rules + ] + + headers = ('Endpoint', 'Methods', 'Rule') + widths = ( + max(len(rule.endpoint) for rule in rules), + max(len(methods) for methods in rule_methods), + max(len(rule.rule) for rule in rules), + ) + widths = [max(len(h), w) for h, w in zip(headers, widths)] + row = '{{0:<{0}}} {{1:<{1}}} {{2:<{2}}}'.format(*widths) - columnformat = '{:<%s} {:<%s} {:<%s}' % (max_rule, max_ep, max_meth) - click.echo(columnformat.format('Route', 'Endpoint', 'Methods')) - under_count = max_rule + max_ep + max_meth + 4 - click.echo('-' * under_count) + click.echo(row.format(*headers).strip()) + click.echo(row.format(*('-' * width for width in widths))) - for rule in sorted_rules: - methods = ', '.join(rule.methods) - click.echo(columnformat.format(rule.rule, rule.endpoint, methods)) + for rule, methods in zip(rules, rule_methods): + click.echo(row.format(rule.endpoint, methods, rule.rule).rstrip()) cli = FlaskGroup(help="""\ diff --git a/tests/test_apps/cliapp/routesapp.py b/tests/test_apps/cliapp/routesapp.py deleted file mode 100644 index 84060546..00000000 --- a/tests/test_apps/cliapp/routesapp.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import absolute_import, print_function - -from flask import Flask - - -noroute_app = Flask('noroute app') -simpleroute_app = Flask('simpleroute app') -only_POST_route_app = Flask('GET route app') - - -@simpleroute_app.route('/simpleroute') -def simple(): - pass - - -@only_POST_route_app.route('/only-post', methods=['POST']) -def only_post(): - pass diff --git a/tests/test_cli.py b/tests/test_cli.py index 56ebce90..ab875cef 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -14,6 +14,7 @@ from __future__ import absolute_import, print_function import os import sys +from functools import partial import click import pytest @@ -195,7 +196,7 @@ def test_flaskgroup(runner): assert result.output == 'flaskgroup\n' -def test_print_exceptions(): +def test_print_exceptions(runner): """Print the stacktrace if the CLI.""" def create_app(info): raise Exception("oh no") @@ -205,7 +206,6 @@ def test_print_exceptions(): def cli(**params): pass - runner = CliRunner() result = runner.invoke(cli, ['--help']) assert result.exit_code == 0 assert 'Exception: oh no' in result.output @@ -213,34 +213,58 @@ def test_print_exceptions(): class TestRoutes: - def test_no_route(self, runner, monkeypatch): - monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:noroute_app') - result = runner.invoke(cli, ['routes'], catch_exceptions=False) - assert result.exit_code == 0 - assert result.output == """\ -Route Endpoint Methods ------------------------------------------------------ -/static/ static HEAD, OPTIONS, GET -""" + @pytest.fixture + def invoke(self, runner): + def create_app(info): + app = Flask(__name__) + app.testing = True - def test_simple_route(self, runner, monkeypatch): - monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:simpleroute_app') - result = runner.invoke(cli, ['routes'], catch_exceptions=False) - assert result.exit_code == 0 - assert result.output == """\ -Route Endpoint Methods ------------------------------------------------------ -/simpleroute simple HEAD, OPTIONS, GET -/static/ static HEAD, OPTIONS, GET -""" + @app.route('/get_post//', methods=['GET', 'POST']) + def yyy_get_post(x, y): + pass + + @app.route('/zzz_post', methods=['POST']) + def aaa_post(): + pass - def test_only_POST_route(self, runner, monkeypatch): - monkeypatch.setitem(os.environ, 'FLASK_APP', 'cliapp.routesapp:only_POST_route_app') - result = runner.invoke(cli, ['routes'], catch_exceptions=False) + return app + + cli = FlaskGroup(create_app=create_app) + return partial(runner.invoke, cli) + + def expect_order(self, order, output): + # skip the header and match the start of each row + for expect, line in zip(order, output.splitlines()[2:]): + # do this instead of startswith for nicer pytest output + assert line[:len(expect)] == expect + + def test_simple(self, invoke): + result = invoke(['routes']) assert result.exit_code == 0 - assert result.output == """\ -Route Endpoint Methods ------------------------------------------------------- -/only-post only_post POST, OPTIONS -/static/ static HEAD, OPTIONS, GET -""" + self.expect_order( + ['aaa_post', 'static', 'yyy_get_post'], + result.output + ) + + def test_sort(self, invoke): + default_output = invoke(['routes']).output + endpoint_output = invoke(['routes', '-s', 'endpoint']).output + assert default_output == endpoint_output + self.expect_order( + ['static', 'yyy_get_post', 'aaa_post'], + invoke(['routes', '-s', 'methods']).output + ) + self.expect_order( + ['yyy_get_post', 'static', 'aaa_post'], + invoke(['routes', '-s', 'rule']).output + ) + self.expect_order( + ['aaa_post', 'yyy_get_post', 'static'], + invoke(['routes', '-s', 'match']).output + ) + + def test_all_methods(self, invoke): + output = invoke(['routes']).output + assert 'GET, HEAD, OPTIONS, POST' not in output + output = invoke(['routes', '--all-methods']).output + assert 'GET, HEAD, OPTIONS, POST' in output From 6032c94aeb090c82ac99b9f886341f61c73b65d0 Mon Sep 17 00:00:00 2001 From: Benjamin Liebald Date: Wed, 2 Nov 2016 13:36:54 -0700 Subject: [PATCH 161/399] Mention existence of register_error_handler in errorpages.rst See https://github.com/pallets/flask/issues/1837 for context. --- docs/patterns/errorpages.rst | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/docs/patterns/errorpages.rst b/docs/patterns/errorpages.rst index fccd4a6f..e7ec6b65 100644 --- a/docs/patterns/errorpages.rst +++ b/docs/patterns/errorpages.rst @@ -54,9 +54,11 @@ can be a different error: a handler for internal server errors will be passed other exception instances as well if they are uncaught. An error handler is registered with the :meth:`~flask.Flask.errorhandler` -decorator and the error code of the exception. Keep in mind that Flask -will *not* set the error code for you, so make sure to also provide the -HTTP status code when returning a response. +decorator and the error code of the exception (alternatively, you can use the +:meth:`~flask.Flask.register_error_handler` function, e.g., when you're +registering error handlers as part of your Application Factory). Keep in mind +that Flask will *not* set the error code for you, so make sure to also provide +the HTTP status code when returning a response.delete_cookie. Please note that if you add an error handler for "500 Internal Server Error", Flask will not trigger it if it's running in Debug mode. @@ -69,6 +71,18 @@ Here an example implementation for a "404 Page Not Found" exception:: def page_not_found(e): return render_template('404.html'), 404 +And, using an application factory pattern (see :ref:`app-factories`):: + + from flask import Flask, render_template + + def page_not_found(e): + return render_template('404.html'), 404 + + def create_app(config_filename): + app = Flask(__name__) + # ... + app.register_error_handler(404, page_not_found) + An example template might be this: .. sourcecode:: html+jinja From 011a4b1899fcb6da15a9d60c3a5c36a73ff92c46 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 30 Apr 2017 08:20:13 -0700 Subject: [PATCH 162/399] clean up error handler docs --- docs/patterns/errorpages.rst | 52 +++++++++++++++++++----------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/docs/patterns/errorpages.rst b/docs/patterns/errorpages.rst index e7ec6b65..1df9c061 100644 --- a/docs/patterns/errorpages.rst +++ b/docs/patterns/errorpages.rst @@ -47,51 +47,53 @@ even if the application behaves correctly: Error Handlers -------------- -An error handler is a function, just like a view function, but it is -called when an error happens and is passed that error. The error is most -likely a :exc:`~werkzeug.exceptions.HTTPException`, but in one case it -can be a different error: a handler for internal server errors will be -passed other exception instances as well if they are uncaught. +An error handler is a function that returns a response when a type of error is +raised, similar to how a view is a function that returns a response when a +request URL is matched. It is passed the instance of the error being handled, +which is most likely a :exc:`~werkzeug.exceptions.HTTPException`. An error +handler for "500 Internal Server Error" will be passed uncaught exceptions in +addition to explicit 500 errors. An error handler is registered with the :meth:`~flask.Flask.errorhandler` -decorator and the error code of the exception (alternatively, you can use the -:meth:`~flask.Flask.register_error_handler` function, e.g., when you're -registering error handlers as part of your Application Factory). Keep in mind -that Flask will *not* set the error code for you, so make sure to also provide -the HTTP status code when returning a response.delete_cookie. +decorator or the :meth:`~flask.Flask.register_error_handler` method. A handler +can be registered for a status code, like 404, or for an exception class. -Please note that if you add an error handler for "500 Internal Server -Error", Flask will not trigger it if it's running in Debug mode. +The status code of the response will not be set to the handler's code. Make +sure to provide the appropriate HTTP status code when returning a response from +a handler. -Here an example implementation for a "404 Page Not Found" exception:: +A handler for "500 Internal Server Error" will not be used when running in +debug mode. Instead, the interactive debugger will be shown. + +Here is an example implementation for a "404 Page Not Found" exception:: from flask import render_template @app.errorhandler(404) def page_not_found(e): + # note that we set the 404 status explicitly return render_template('404.html'), 404 -And, using an application factory pattern (see :ref:`app-factories`):: +When using the :ref:`application factory pattern `:: from flask import Flask, render_template - + def page_not_found(e): return render_template('404.html'), 404 - + def create_app(config_filename): app = Flask(__name__) - # ... app.register_error_handler(404, page_not_found) + return app An example template might be this: .. sourcecode:: html+jinja - {% extends "layout.html" %} - {% block title %}Page Not Found{% endblock %} - {% block body %} -

    Page Not Found

    -

    What you were looking for is just not there. -

    go somewhere nice - {% endblock %} - + {% extends "layout.html" %} + {% block title %}Page Not Found{% endblock %} + {% block body %} +

    Page Not Found

    +

    What you were looking for is just not there. +

    go somewhere nice + {% endblock %} From 75b85656ddad86cbf0c3adb1128b758242793929 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 4 May 2017 18:24:04 -0700 Subject: [PATCH 163/399] optionally enable sphinxcontrib.log_cabinet collapses old changelog directives closes #1704 closes #1867 --- docs/conf.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index f53d72fa..47168c65 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,6 +38,14 @@ extensions = [ 'flaskdocext' ] +try: + __import__('sphinxcontrib.log_cabinet') +except ImportError: + print('sphinxcontrib-log-cabinet is not installed.') + print('Changelog directives will not be re-organized.') +else: + extensions.append('sphinxcontrib.log_cabinet') + # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] From 160999e88241137c73aaa49e352dcbf2829fad5a Mon Sep 17 00:00:00 2001 From: wangbing Date: Wed, 10 May 2017 00:34:36 +0800 Subject: [PATCH 164/399] Removed unused import --- flask/json.py | 1 - 1 file changed, 1 deletion(-) diff --git a/flask/json.py b/flask/json.py index bf8a8843..a029e73a 100644 --- a/flask/json.py +++ b/flask/json.py @@ -13,7 +13,6 @@ import uuid from datetime import date from .globals import current_app, request from ._compat import text_type, PY2 -from .ctx import has_request_context from werkzeug.http import http_date from jinja2 import Markup From dfb03c5673e97ee95289b844c13dbf506ea99e71 Mon Sep 17 00:00:00 2001 From: Xephyr826 Date: Wed, 10 May 2017 22:38:22 -0700 Subject: [PATCH 165/399] Improve Routing section Edited the entire section for clarity and concision. I rewrote sentences to make them shorter and to reduce ambiguity. Added a code sample to show the path converter type Removed the HTTP method overview. Although it was well written, the overview wasn't necessary in the quickstart. Readers can easily find an overview elsewhere. --- docs/quickstart.rst | 152 ++++++++++---------------------------------- 1 file changed, 34 insertions(+), 118 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 09365496..76f2af42 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -159,14 +159,9 @@ Have another debugger in mind? See :ref:`working-with-debuggers`. Routing ------- -Modern web applications have beautiful URLs. This helps people remember -the URLs, which is especially handy for applications that are used from -mobile devices with slower network connections. If the user can directly -go to the desired page without having to hit the index page it is more -likely they will like the page and come back next time. +Modern web applications use meaningful URLs to help users. Users are more likely to like a page and come back if the page uses a meaningful URL they can remember and use to directly visit a page. -As you have seen above, the :meth:`~flask.Flask.route` decorator is used to -bind a function to a URL. Here are some basic examples:: +As you saw in the minimal application, you can use the :meth:`~flask.Flask.route` decorator to bind a function to a meaningful URL. For example:: @app.route('/') def index(): @@ -176,16 +171,12 @@ bind a function to a URL. Here are some basic examples:: def hello(): return 'Hello, World' -But there is more to it! You can make certain parts of the URL dynamic and -attach multiple rules to a function. +You can do more! You can make parts of the URL dynamic and attach multiple rules to a function. Variable Rules `````````````` -To add variable parts to a URL you can mark these special sections as -````. Such a part is then passed as a keyword argument to your -function. Optionally a converter can be used by specifying a rule with -````. Here are some nice examples:: +You can add variable sections to a URL by marking sections with ````. Your function then receives the ```` as a keyword argument. Optionally, you can use a converter to specify a rule with ````. For example:: @app.route('/user/') def show_user_profile(username): @@ -197,22 +188,25 @@ function. Optionally a converter can be used by specifying a rule with # show the post with the given id, the id is an integer return 'Post %d' % post_id -The following converters exist: + @app.route('/path/') + def show_subpath(subpath): + # show the subpath after /path/ + return 'Subpath %s' % subpath + +Converter types: =========== =============================================== -`string` accepts any text without a slash (the default) +`string` (default) accepts any text without a slash `int` accepts integers -`float` like ``int`` but for floating point values -`path` like the default but also accepts slashes +`float` accepts floating point values +`path` like `string` but also accepts slashes `any` matches one of the items provided `uuid` accepts UUID strings =========== =============================================== .. admonition:: Unique URLs / Redirection Behavior - Flask's URL rules are based on Werkzeug's routing module. The idea - behind that module is to ensure beautiful and unique URLs based on - precedents laid down by Apache and earlier HTTP servers. + Flask's URL rules are based on Werkzeug's routing module. The routing module creates meaningful and unique URLs based on precedents laid down by Apache and earlier HTTP servers. Take these two rules:: @@ -224,15 +218,9 @@ The following converters exist: def about(): return 'The about page' - Though they look rather similar, they differ in their use of the trailing - slash in the URL *definition*. In the first case, the canonical URL for the - ``projects`` endpoint has a trailing slash. In that sense, it is similar to - a folder on a filesystem. Accessing it without a trailing slash will cause - Flask to redirect to the canonical URL with the trailing slash. + Though they look similar, they differ in their use of the trailing slash in the URL *definition*. In the first case, the canonical URL for the ``projects`` endpoint uses a trailing slash. It's similar to a folder in a file system, and if you access the URL without a trailing slash, Flask redirects you to the canonical URL with the trailing slash. - In the second case, however, the URL is defined without a trailing slash, - rather like the pathname of a file on UNIX-like systems. Accessing the URL - with a trailing slash will produce a 404 "Not Found" error. + In the second case, however, the URL definition lacks a trailing slash like the pathname of a file on UNIX-like systems. Accessing the URL with a trailing slash produces a 404 “Not Found” error. This behavior allows relative URLs to continue working even if the trailing slash is omitted, consistent with how Apache and other servers work. Also, @@ -245,12 +233,19 @@ The following converters exist: URL Building ```````````` -If it can match URLs, can Flask also generate them? Of course it can. To -build a URL to a specific function you can use the :func:`~flask.url_for` -function. It accepts the name of the function as first argument and a number -of keyword arguments, each corresponding to the variable part of the URL rule. -Unknown variable parts are appended to the URL as query parameters. Here are -some examples:: +Flask can match URLs, but can it also generate them? Yes! To build a URL to a specific function, you can use the :func:`~flask.url_for` function. It accepts the name of the function as its first argument and any number of keyword arguments, each corresponding to a variable part of the URL rule. Unknown variable parts are appended to the URL as query parameters. + +Why would you want to build URLs using the URL reversing function +:func:`~flask.url_for` instead of hard-coding them into your templates? Three good reasons: + +1. Reversing is often more descriptive than hard-coding the URLs. More importantly, you can change your URLs in one go instead of needing to remember to manually change hard-coded URLs. +2. URL building handles escaping of special characters and Unicode data transparently. +3. If your application is placed outside the URL root, for example, in ``/myapplication`` instead of ``/``, :func:`~flask.url_for` properly handles that for you. + + +For example, here we use the :meth:`~flask.Flask.test_request_context` method to try out :func:`~flask.url_for`. :meth:`~flask.Flask.test_request_context` tells Flask to behave as though it's handling a request even while we use a Python shell. Also see, :ref:`context-locals`. + +Example:: >>> from flask import Flask, url_for >>> app = Flask(__name__) @@ -274,98 +269,19 @@ some examples:: /login?next=/ /user/John%20Doe -(This also uses the :meth:`~flask.Flask.test_request_context` method, explained -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: - -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 - remember to change URLs all over the place. -2. URL building will handle escaping of special characters and Unicode - data transparently for you, so you don't have to deal with them. -3. If your application is placed outside the URL root - say, in - ``/myapplication`` instead of ``/`` - :func:`~flask.url_for` will handle - that properly for you. - - HTTP Methods ```````````` -HTTP (the protocol web applications are speaking) knows different methods for -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 +Web application communicate using HTTP and use different methods for accessing URLs. You should familiarize yourself with the HTTP methods as you work with Flask. By default, a route only answers to ``GET`` requests. You can use the ``methods`` argument of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods. For example:: @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': - return do_the_login() + do_the_login() else: - return show_the_login_form() - -If ``GET`` is present, ``HEAD`` will be added automatically for you. You -don't have to deal with that. It will also make sure that ``HEAD`` requests -are handled as the `HTTP RFC`_ (the document describing the HTTP -protocol) demands, so you can completely ignore that part of the HTTP -specification. Likewise, as of Flask 0.6, ``OPTIONS`` is implemented for you -automatically as well. - -You have no idea what an HTTP method is? Worry not, here is a quick -introduction to HTTP methods and why they matter: - -The HTTP method (also often called "the verb") tells the server what the -client wants to *do* with the requested page. The following methods are -very common: - -``GET`` - The browser tells the server to just *get* the information stored on - that page and send it. This is probably the most common method. - -``HEAD`` - The browser tells the server to get the information, but it is only - interested in the *headers*, not the content of the page. An - application is supposed to handle that as if a ``GET`` request was - received but to not deliver the actual content. In Flask you don't - have to deal with that at all, the underlying Werkzeug library handles - that for you. - -``POST`` - The browser tells the server that it wants to *post* some new - information to that URL and that the server must ensure the data is - stored and only stored once. This is how HTML forms usually - transmit data to the server. - -``PUT`` - Similar to ``POST`` but the server might trigger the store procedure - multiple times by overwriting the old values more than once. Now you - might be asking why this is useful, but there are some good reasons - to do it this way. Consider that the connection is lost during - transmission: in this situation a system between the browser and the - server might receive the request safely a second time without breaking - things. With ``POST`` that would not be possible because it must only - be triggered once. - -``DELETE`` - Remove the information at the given location. - -``OPTIONS`` - Provides a quick way for a client to figure out which methods are - supported by this URL. Starting with Flask 0.6, this is implemented - for you automatically. - -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 systems -use it. + show_the_login_form() + +If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise, as of Flask 0.6, ``OPTIONS`` is automatically implemented for you. .. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt From a80eab70ba96d7d43c2ca5dc742597320aebe104 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 11 May 2017 12:35:03 -0700 Subject: [PATCH 166/399] Update CONTRIBUTING.rst basic pytest can't handle examples, pass tests dir --- CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 66766512..852e44be 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -54,7 +54,7 @@ Install Flask as an editable package using the current source:: Then you can run the testsuite with:: - pytest + pytest tests/ With only pytest installed, a large part of the testsuite will get skipped though. Whether this is relevant depends on which part of Flask you're working From 1fb43e3be47ab24cc32396e28a6ad3734e9109e9 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 11 May 2017 13:54:37 -0700 Subject: [PATCH 167/399] rewrite installation docs discuss python version discuss all dependencies prefer python 3 in instructions [ci skip] --- docs/installation.rst | 231 ++++++++++++++++++++---------------------- 1 file changed, 108 insertions(+), 123 deletions(-) diff --git a/docs/installation.rst b/docs/installation.rst index 38094ded..cd869b9a 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -3,188 +3,173 @@ Installation ============ -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. -Jinja2 renders templates. +Python Version +-------------- -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. +We recommend using the latest version of Python 3. Flask supports Python 3.3 +and newer, Python 2.6 and newer, and PyPy. -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`. +Dependencies +------------ -.. _virtualenv: +These distributions will be installed automatically when installing Flask. -virtualenv ----------- +* `Werkzeug`_ implements WSGI, the standard Python interface between + applications and servers. +* `Jinja`_ is a template language that renders the pages your application + serves. +* `MarkupSafe`_ comes with Jinja. It escapes untrusted input when rendering + templates to avoid injection attacks. +* `ItsDangerous`_ securely signs data to ensure its integrity. This is used + to protect Flask's session cookie. +* `Click`_ is a framework for writing command line applications. It provides + the ``flask`` command and allows adding custom management commands. -Virtualenv is probably what you want to use during development, and if you have -shell access to your production machines, you'll probably want to use it there, -too. +.. _Werkzeug: http://werkzeug.pocoo.org/ +.. _Jinja: http://jinja.pocoo.org/ +.. _MarkupSafe: https://pypi.python.org/pypi/MarkupSafe +.. _ItsDangerous: https://pythonhosted.org/itsdangerous/ +.. _Click: http://click.pocoo.org/ -What problem does virtualenv solve? If you like Python as much as I do, -chances are you want to use it for other projects besides Flask-based web -applications. But the more projects you have, the more likely it is that you -will be working with different versions of Python itself, or at least different -versions of Python libraries. Let's face it: quite often libraries break -backwards compatibility, and it's unlikely that any serious application will -have zero dependencies. So what do you do if two or more of your projects have -conflicting dependencies? +Optional dependencies +~~~~~~~~~~~~~~~~~~~~~ -Virtualenv to the rescue! Virtualenv enables multiple side-by-side -installations of Python, one for each project. It doesn't actually install -separate copies of Python, but it does provide a clever way to keep different -project environments isolated. Let's see how virtualenv works. +These distributions will not be installed automatically. Flask will detect and +use them if you install them. +* `Blinker`_ provides support for :ref:`signals`. +* `SimpleJSON`_ is a fast JSON implementation that is compatible with + Python's ``json`` module. It is preferred for JSON operations if it is + installed. -.. admonition:: A note on python3 and virtualenv +.. _Blinker: https://pythonhosted.org/blinker/ +.. _SimpleJSON: https://simplejson.readthedocs.io/ - If you are planning on using python3 with the virtualenv, you don't need to - install ``virtualenv``. Python3 has built-in support for virtual environments. +Virtual environments +-------------------- -If you are on Mac OS X or Linux, chances are that the following -command will work for you:: +Use a virtual environment to manage the dependencies for your project, both in +development and in production. - $ sudo pip install virtualenv +What problem does a virtual environment solve? The more Python projects you +have, the more likely it is that you need to work with different versions of +Python libraries, or even Python itself. Newer versions of libraries for one +project can break compatibility in another project. -It will probably install virtualenv on your system. Maybe it's even -in your package manager. If you use Ubuntu, try:: +Virtual environments are independent groups of Python libraries, one for each +project. Packages installed for one project will not affect other projects or +the operating system's packages. - $ sudo apt-get install python-virtualenv +Python 3 comes bundled with the :mod:`venv` module to create virtual +environments. If you're using a modern version of Python, you can continue on +to the next section. -If you are on Windows and don't have the ``easy_install`` command, you must -install it first. Check the :ref:`windows-easy-install` section for more -information about how to do that. Once you have it installed, run the same -commands as above, but without the ``sudo`` prefix. +If you're using Python 2, see :ref:`install-install-virtualenv` first. -Creating a virtual environment -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.. _install-create-env: -Once you have virtualenv installed, just fire up a shell and create -your own environment. I usually create a project folder and a :file:`virtenv` -folder within:: +Create an environment +~~~~~~~~~~~~~~~~~~~~~ - $ mkdir myproject - $ cd myproject +Create a project folder and a :file:`venv` folder within: -There is a little change in how you create a virtualenv depending on which python-version you are currently using. +.. code-block:: sh -**Python2** + mkdir myproject + cd myproject + python3 -m venv venv -:: +On Windows: - $ virtualenv virtenv - New python executable in virtenv/bin/python - Installing setuptools, pip............done. +.. code-block:: bat -**Python 3.6 and above** + py -3 -m venv venv -:: +If you needed to install virtualenv because you are on an older version of +Python, use the following command instead: - $ python3 -m venv virtenv +.. code-block:: sh -Activating a virtual environment -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + virtualenv venv -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:: +On Windows: - $ . virtenv/bin/activate +.. code-block:: bat -If you are a Windows user, the following command is for you:: + \Python27\Scripts\virtualenv.exe venv - $ virtenv\Scripts\activate +Activate the environment +~~~~~~~~~~~~~~~~~~~~~~~~ -Either way, you should now be using your virtualenv (notice how the prompt of -your shell has changed to show the active environment). +Before you work on your project, activate the corresponding environment: -And if you want to go back to the real world, use the following command:: +.. code-block:: sh - $ deactivate + . venv/bin/activate -After doing this, the prompt of your shell should be as familiar as before. +On Windows: -Now, let's move on. Enter the following command to get Flask activated in your -virtualenv:: +.. code-block:: bat - $ pip install Flask + venv\Scripts\activate -A few seconds later and you are good to go. +Your shell prompt will change to show the name of the activated environment. +Install Flask +------------- -System-Wide Installation ------------------------- +Within the activated environment, use the following command to install Flask: -This is possible as well, though I do not recommend it. Just run -``pip`` with root privileges:: +.. code-block:: sh - $ sudo pip install Flask + pip install Flask -(On Windows systems, run it in a command-prompt window with administrator -privileges, and leave out ``sudo``.) +Living on the edge +~~~~~~~~~~~~~~~~~~ +If you want to work with the latest Flask code before it's released, install or +update the code from the master branch: -Living on the Edge ------------------- +.. code-block:: sh -If you want to work with the latest version of Flask, there are two ways: you -can either let ``pip`` pull in the development version, or you can tell -it to operate on a git checkout. Either way, virtualenv is recommended. + pip install -U https://github.com/pallets/flask/archive/master.tar.gz -Get the git checkout in a new virtualenv and run in development mode:: +.. _install-install-virtualenv: - $ git clone https://github.com/pallets/flask.git - Initialized empty Git repository in ~/dev/flask/.git/ - $ cd flask - $ virtualenv virtenv - New python executable in virtenv/bin/python - Installing setuptools, pip............done. - $ . virtenv/bin/activate - $ python setup.py develop - ... - Finished processing dependencies for Flask +Install virtualenv +------------------ -This will pull in the dependencies and activate the git head as the current -version inside the virtualenv. Then all you have to do is run ``git pull -origin`` to update to the latest version. +If you are using Python 2, the venv module is not available. Instead, +install `virtualenv`_. -.. _windows-easy-install: +On Linux, virtualenv is provided by your package manager: -`pip` and `setuptools` on Windows ---------------------------------- +.. code-block:: sh -Sometimes getting the standard "Python packaging tools" like ``pip``, ``setuptools`` -and ``virtualenv`` can be a little trickier, but nothing very hard. The crucial -package you will need is pip - this will let you install -anything else (like virtualenv). Fortunately there is a "bootstrap script" -you can run to install. + # Debian, Ubuntu + sudo apt-get install python-virtualenv -If you don't currently have ``pip``, then `get-pip.py` will install it for you. + # CentOS, Fedora + sudo yum install python-virtualenv -`get-pip.py`_ + # Arch + sudo pacman -S python-virtualenv -It should be double-clickable once you download it. If you already have ``pip``, -you can upgrade them by running:: +If you are on Mac OS X or Windows, download `get-pip.py`_, then: - > pip install --upgrade pip setuptools +.. code-block:: sh -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!). + sudo python2 Downloads/get-pip.py + sudo python2 -m pip install virtualenv -To fix this, you should be able to navigate to your Python install directory -(e.g :file:`C:\Python27`), then go to :file:`Tools`, then :file:`Scripts`, then find the -:file:`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. +On Windows, as an administrator: -Finally, to install `virtualenv`_, you can simply run:: +.. code-block:: bat - > pip install virtualenv + \Python27\python.exe Downloads\get-pip.py + \Python27\python.exe -m pip install virtualenv -Then you can be off on your way following the installation instructions above. +Now you can continue to :ref:`install-create-env`. +.. _virtualenv: https://virtualenv.pypa.io/ .. _get-pip.py: https://bootstrap.pypa.io/get-pip.py From 2592f927a00f39ca089f67144a1e7f200ad213d1 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 11 May 2017 22:31:19 -0700 Subject: [PATCH 168/399] wrap lines tighten up wording remove any converter from quickstart use correct rst code syntax --- docs/quickstart.rst | 148 ++++++++++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 60 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 76f2af42..7bdb67e4 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -159,9 +159,11 @@ Have another debugger in mind? See :ref:`working-with-debuggers`. Routing ------- -Modern web applications use meaningful URLs to help users. Users are more likely to like a page and come back if the page uses a meaningful URL they can remember and use to directly visit a page. +Modern web applications use meaningful URLs to help users. Users are more +likely to like a page and come back if the page uses a meaningful URL they can +remember and use to directly visit a page. -As you saw in the minimal application, you can use the :meth:`~flask.Flask.route` decorator to bind a function to a meaningful URL. For example:: +Use the :meth:`~flask.Flask.route` decorator to bind a function to a URL. :: @app.route('/') def index(): @@ -171,12 +173,16 @@ As you saw in the minimal application, you can use the :meth:`~flask.Flask.route def hello(): return 'Hello, World' -You can do more! You can make parts of the URL dynamic and attach multiple rules to a function. +You can do more! You can make parts of the URL dynamic and attach multiple +rules to a function. Variable Rules `````````````` -You can add variable sections to a URL by marking sections with ````. Your function then receives the ```` as a keyword argument. Optionally, you can use a converter to specify a rule with ````. For example:: +You can add variable sections to a URL by marking sections with +````. Your function then receives the ```` +as a keyword argument. Optionally, you can use a converter to specify the type +of the argument like ````. :: @app.route('/user/') def show_user_profile(username): @@ -195,75 +201,91 @@ You can add variable sections to a URL by marking sections with ``>> from flask import Flask, url_for - >>> app = Flask(__name__) - >>> @app.route('/') - ... def index(): pass - ... - >>> @app.route('/login') - ... def login(): pass - ... - >>> @app.route('/user/') - ... def profile(username): pass - ... - >>> with app.test_request_context(): - ... print(url_for('index')) - ... print(url_for('login')) - ... print(url_for('login', next='/')) - ... print(url_for('profile', username='John Doe')) - ... +:func:`~flask.url_for` instead of hard-coding them into your templates? + +1. Reversing is often more descriptive than hard-coding the URLs. +2. You can change your URLs in one go instead of needing to remember to + manually change hard-coded URLs. +3. URL building handles escaping of special characters and Unicode data + transparently. +4. If your application is placed outside the URL root, for example, in + ``/myapplication`` instead of ``/``, :func:`~flask.url_for` properly + handles that for you. + +For example, here we use the :meth:`~flask.Flask.test_request_context` method +to try out :func:`~flask.url_for`. :meth:`~flask.Flask.test_request_context` +tells Flask to behave as though it's handling a request even while we use a +Python shell. See :ref:`context-locals`. :: + + from flask import Flask, url_for + + app = Flask(__name__) + + @app.route('/') + def index(): + return 'index' + + @app.route('/login') + def login(): + return 'login' + + @app.route('/user/') + def profile(username): + return '{}'s profile'.format(username) + + with app.test_request_context(): + print(url_for('index')) + print(url_for('login')) + print(url_for('login', next='/')) + print(url_for('profile', username='John Doe')) + / /login /login?next=/ @@ -272,7 +294,11 @@ Example:: HTTP Methods ```````````` -Web application communicate using HTTP and use different methods for accessing URLs. You should familiarize yourself with the HTTP methods as you work with Flask. By default, a route only answers to ``GET`` requests. You can use the ``methods`` argument of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods. For example:: +Web applications use different HTTP methods when accessing URLs. You should +familiarize yourself with the HTTP methods as you work with Flask. By default, +a route only answers to ``GET`` requests. You can use the ``methods`` argument +of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods. +:: @app.route('/login', methods=['GET', 'POST']) def login(): @@ -281,7 +307,9 @@ Web application communicate using HTTP and use different methods for accessing U else: show_the_login_form() -If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise, as of Flask 0.6, ``OPTIONS`` is automatically implemented for you. +If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method +and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise, +``OPTIONS`` is automatically implemented for you. .. _HTTP RFC: https://www.ietf.org/rfc/rfc2068.txt From cc59f2b2046fdc5ef280b95aaa22c26c3e0fba46 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 11 May 2017 22:48:21 -0700 Subject: [PATCH 169/399] clean up deferred callback doc remove doc about writing after_this_request [ci skip] --- docs/patterns/deferredcallbacks.rst | 80 ++++++++++------------------- 1 file changed, 26 insertions(+), 54 deletions(-) diff --git a/docs/patterns/deferredcallbacks.rst b/docs/patterns/deferredcallbacks.rst index a79dc913..bc20cdd6 100644 --- a/docs/patterns/deferredcallbacks.rst +++ b/docs/patterns/deferredcallbacks.rst @@ -3,71 +3,43 @@ Deferred Request Callbacks ========================== -One of the design principles of Flask is that response objects are created -and passed down a chain of potential callbacks that can modify them or -replace them. When the request handling starts, there is no response -object yet. It is created as necessary either by a view function or by -some other component in the system. - -But what happens if you want to modify the response at a point where the -response does not exist yet? A common example for that would be a -before-request function that wants to set a cookie on the response object. - -One way is to avoid the situation. Very often that is possible. For -instance you can try to move that logic into an after-request callback -instead. Sometimes however moving that code there is just not a very -pleasant experience or makes code look very awkward. - -As an alternative possibility you can attach a bunch of callback functions -to the :data:`~flask.g` object and call them at the end of the request. -This way you can defer code execution from anywhere in the application. - - -The Decorator -------------- - -The following decorator is the key. It registers a function on a list on -the :data:`~flask.g` object:: - - from flask import g - - def after_this_request(f): - if not hasattr(g, 'after_request_callbacks'): - g.after_request_callbacks = [] - g.after_request_callbacks.append(f) - return f - - -Calling the Deferred --------------------- - -Now you can use the `after_this_request` decorator to mark a function to -be called at the end of the request. But we still need to call them. For -this the following function needs to be registered as -:meth:`~flask.Flask.after_request` callback:: - - @app.after_request - def call_after_request_callbacks(response): - for callback in getattr(g, 'after_request_callbacks', ()): - callback(response) - return response - - -A Practical Example -------------------- +One of the design principles of Flask is that response objects are created and +passed down a chain of potential callbacks that can modify them or replace +them. When the request handling starts, there is no response object yet. It is +created as necessary either by a view function or by some other component in +the system. + +What happens if you want to modify the response at a point where the response +does not exist yet? A common example for that would be a +:meth:`~flask.Flask.before_request` callback that wants to set a cookie on the +response object. + +One way is to avoid the situation. Very often that is possible. For instance +you can try to move that logic into a :meth:`~flask.Flask.after_request` +callback instead. However, sometimes moving code there makes it more +more complicated or awkward to reason about. + +As an alternative, you can use :func:`~flask.after_this_request` to register +callbacks that will execute after only the current request. This way you can +defer code execution from anywhere in the application, based on the current +request. At any time during a request, we can register a function to be called at the -end of the request. For example you can remember the current language of the -user in a cookie in the before-request function:: +end of the request. For example you can remember the current language of the +user in a cookie in a :meth:`~flask.Flask.before_request` callback:: from flask import request, after_this_request @app.before_request def detect_user_language(): language = request.cookies.get('user_lang') + if language is None: language = guess_language_from_request() + + # when the response exists, set a cookie with the language @after_this_request def remember_language(response): response.set_cookie('user_lang', language) + g.language = language From c3d49e29ea42d2f468bc780c15683ca197b50d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Oliveira?= Date: Sun, 4 Dec 2016 18:29:11 +0000 Subject: [PATCH 170/399] show warning if session cookie domain is ip closes #2007 --- flask/helpers.py | 20 ++++++++++++++++++++ flask/sessions.py | 6 +++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/flask/helpers.py b/flask/helpers.py index 828f5840..4794ef97 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -976,3 +976,23 @@ def total_seconds(td): :rtype: int """ return td.days * 60 * 60 * 24 + td.seconds + +def is_ip(ip): + """Returns the if the string received is an IP or not. + + :param string: the string to check if it an IP or not + :param var_name: the name of the string that is being checked + + :returns: True if string is an IP, False if not + :rtype: boolean + """ + import socket + + for family in (socket.AF_INET, socket.AF_INET6): + try: + socket.inet_pton(family, ip) + except socket.error: + pass + else: + return True + return False diff --git a/flask/sessions.py b/flask/sessions.py index 525ff246..f1729674 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -11,13 +11,14 @@ import uuid import hashlib +from warnings import warn from base64 import b64encode, b64decode from datetime import datetime from werkzeug.http import http_date, parse_date from werkzeug.datastructures import CallbackDict from . import Markup, json from ._compat import iteritems, text_type -from .helpers import total_seconds +from .helpers import total_seconds, is_ip from itsdangerous import URLSafeTimedSerializer, BadSignature @@ -336,6 +337,9 @@ class SecureCookieSessionInterface(SessionInterface): def save_session(self, app, session, response): domain = self.get_cookie_domain(app) + if domain is not None: + if is_ip(domain): + warnings.warn("IP introduced in SESSION_COOKIE_DOMAIN", RuntimeWarning) path = self.get_cookie_path(app) # Delete case. If there is no session we bail early. From f75ad9fca2e17c4777a3d7efc65f3ccab261e22c Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 13 May 2017 21:31:46 -0700 Subject: [PATCH 171/399] refactor session cookie domain logic cache result of session cookie domain add warnings for session cookie domain issues add changelog --- CHANGES | 7 ++++ flask/helpers.py | 18 +++++----- flask/sessions.py | 83 ++++++++++++++++++++++++++++++--------------- tests/test_basic.py | 36 ++++++++++++++++++++ 4 files changed, 109 insertions(+), 35 deletions(-) diff --git a/CHANGES b/CHANGES index ddab541f..a6eceb87 100644 --- a/CHANGES +++ b/CHANGES @@ -34,6 +34,12 @@ Major release, unreleased type is invalid. (`#2256`_) - Add ``routes`` CLI command to output routes registered on the application. (`#2259`_) +- Show warning when session cookie domain is a bare hostname or an IP + address, as these may not behave properly in some browsers, such as Chrome. + (`#2282`_) +- Allow IP address as exact session cookie domain. (`#2282`_) +- ``SESSION_COOKIE_DOMAIN`` is set if it is detected through ``SERVER_NAME``. + (`#2282`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1898: https://github.com/pallets/flask/pull/1898 @@ -43,6 +49,7 @@ Major release, unreleased .. _#2254: https://github.com/pallets/flask/pull/2254 .. _#2256: https://github.com/pallets/flask/pull/2256 .. _#2259: https://github.com/pallets/flask/pull/2259 +.. _#2282: https://github.com/pallets/flask/pull/2282 Version 0.12.1 -------------- diff --git a/flask/helpers.py b/flask/helpers.py index 4794ef97..51c3b064 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -10,6 +10,7 @@ """ import os +import socket import sys import pkgutil import posixpath @@ -977,22 +978,23 @@ def total_seconds(td): """ return td.days * 60 * 60 * 24 + td.seconds -def is_ip(ip): - """Returns the if the string received is an IP or not. - :param string: the string to check if it an IP or not - :param var_name: the name of the string that is being checked +def is_ip(value): + """Determine if the given string is an IP address. - :returns: True if string is an IP, False if not - :rtype: boolean + :param value: value to check + :type value: str + + :return: True if string is an IP address + :rtype: bool """ - import socket for family in (socket.AF_INET, socket.AF_INET6): try: - socket.inet_pton(family, ip) + socket.inet_pton(family, value) except socket.error: pass else: return True + return False diff --git a/flask/sessions.py b/flask/sessions.py index f1729674..9fef6a9d 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -11,7 +11,7 @@ import uuid import hashlib -from warnings import warn +import warnings from base64 import b64encode, b64decode from datetime import datetime from werkzeug.http import http_date, parse_date @@ -201,30 +201,62 @@ class SessionInterface(object): return isinstance(obj, self.null_session_class) def get_cookie_domain(self, app): - """Helpful helper method that returns the cookie domain that should - be used for the session cookie if session cookies are used. + """Returns the domain that should be set for the session cookie. + + Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise + falls back to detecting the domain based on ``SERVER_NAME``. + + Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` is + updated to avoid re-running the logic. """ - if app.config['SESSION_COOKIE_DOMAIN'] is not None: - return app.config['SESSION_COOKIE_DOMAIN'] - if app.config['SERVER_NAME'] is not None: - # chop off the port which is usually not supported by browsers - rv = '.' + app.config['SERVER_NAME'].rsplit(':', 1)[0] - - # Google chrome does not like cookies set to .localhost, so - # we just go with no domain then. Flask documents anyways that - # cross domain cookies need a fully qualified domain name - if rv == '.localhost': - rv = None - - # If we infer the cookie domain from the server name we need - # to check if we are in a subpath. In that case we can't - # set a cross domain cookie. - if rv is not None: - path = self.get_cookie_path(app) - if path != '/': - rv = rv.lstrip('.') - - return rv + + rv = app.config['SESSION_COOKIE_DOMAIN'] + + # set explicitly, or cached from SERVER_NAME detection + # if False, return None + if rv is not None: + return rv if rv else None + + rv = app.config['SERVER_NAME'] + + # server name not set, cache False to return none next time + if not rv: + app.config['SESSION_COOKIE_DOMAIN'] = False + return None + + # chop off the port which is usually not supported by browsers + # remove any leading '.' since we'll add that later + rv = rv.rsplit(':', 1)[0].lstrip('.') + + if '.' not in rv: + # Chrome doesn't allow names without a '.' + # this should only come up with localhost + # hack around this by not setting the name, and show a warning + warnings.warn( + '"{rv}" is not a valid cookie domain, it must contain a ".".' + ' Add an entry to your hosts file, for example' + ' "{rv}.localdomain", and use that instead.'.format(rv=rv) + ) + app.config['SESSION_COOKIE_DOMAIN'] = False + return None + + ip = is_ip(rv) + + if ip: + warnings.warn( + 'The session cookie domain is an IP address. This may not work' + ' as intended in some browsers. Add an entry to your hosts' + ' file, for example "localhost.localdomain", and use that' + ' instead.' + ) + + # if this is not an ip and app is mounted at the root, allow subdomain + # matching by adding a '.' prefix + if self.get_cookie_path(app) == '/' and not ip: + rv = '.' + rv + + app.config['SESSION_COOKIE_DOMAIN'] = rv + return rv def get_cookie_path(self, app): """Returns the path for which the cookie should be valid. The @@ -337,9 +369,6 @@ class SecureCookieSessionInterface(SessionInterface): def save_session(self, app, session, response): domain = self.get_cookie_domain(app) - if domain is not None: - if is_ip(domain): - warnings.warn("IP introduced in SESSION_COOKIE_DOMAIN", RuntimeWarning) path = self.get_cookie_path(app) # Delete case. If there is no session we bail early. diff --git a/tests/test_basic.py b/tests/test_basic.py index 163b83cf..80091bd6 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -351,6 +351,42 @@ def test_session_using_session_settings(): assert 'httponly' not in cookie +def test_session_localhost_warning(recwarn): + app = flask.Flask(__name__) + app.config.update( + SECRET_KEY='testing', + SERVER_NAME='localhost:5000', + ) + + @app.route('/') + def index(): + flask.session['testing'] = 42 + return 'testing' + + rv = app.test_client().get('/', 'http://localhost:5000/') + assert 'domain' not in rv.headers['set-cookie'].lower() + w = recwarn.pop(UserWarning) + assert '"localhost" is not a valid cookie domain' in str(w.message) + + +def test_session_ip_warning(recwarn): + app = flask.Flask(__name__) + app.config.update( + SECRET_KEY='testing', + SERVER_NAME='127.0.0.1:5000', + ) + + @app.route('/') + def index(): + flask.session['testing'] = 42 + return 'testing' + + rv = app.test_client().get('/', 'http://127.0.0.1:5000/') + assert 'domain=127.0.0.1' in rv.headers['set-cookie'].lower() + w = recwarn.pop(UserWarning) + assert 'cookie domain is an IP' in str(w.message) + + def test_missing_session(): app = flask.Flask(__name__) From d5a88bf0d36a2648cfc224884a144df7870894ab Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 15 May 2017 12:40:09 -0700 Subject: [PATCH 172/399] explain when to use a task queue remove deprecated abstract attr from celery add explanation of example task [ci skip] --- docs/patterns/celery.rst | 78 ++++++++++++++++++++++------------------ 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/docs/patterns/celery.rst b/docs/patterns/celery.rst index 548da29b..c3098a9e 100644 --- a/docs/patterns/celery.rst +++ b/docs/patterns/celery.rst @@ -1,24 +1,27 @@ -Celery Based Background Tasks -============================= +Celery Background Tasks +======================= -Celery is a task queue for Python with batteries included. It used to -have a Flask integration but it became unnecessary after some -restructuring of the internals of Celery with Version 3. This guide fills -in the blanks in how to properly use Celery with Flask but assumes that -you generally already read the `First Steps with Celery -`_ -guide in the official Celery documentation. +If your application has a long running task, such as processing some uploaded +data or sending email, you don't want to wait for it to finish during a +request. Instead, use a task queue to send the necessary data to another +process that will run the task in the background while the request returns +immediately. -Installing Celery ------------------ +Celery is a powerful task queue that can be used for simple background tasks +as well as complex multi-stage programs and schedules. This guide will show you +how to configure Celery using Flask, but assumes you've already read the +`First Steps with Celery `_ +guide in the Celery documentation. -Celery is on the Python Package Index (PyPI), so it can be installed with -standard Python tools like :command:`pip` or :command:`easy_install`:: +Install +------- + +Celery is a separate Python package. Install it from PyPI using pip:: $ pip install celery -Configuring Celery ------------------- +Configure +--------- The first thing you need is a Celery instance, this is called the celery application. It serves the same purpose as the :class:`~flask.Flask` @@ -36,15 +39,18 @@ This is all that is necessary to properly integrate Celery with Flask:: from celery import Celery def make_celery(app): - celery = Celery(app.import_name, backend=app.config['CELERY_RESULT_BACKEND'], - broker=app.config['CELERY_BROKER_URL']) + celery = Celery( + app.import_name, + backend=app.config['CELERY_RESULT_BACKEND'], + broker=app.config['CELERY_BROKER_URL'] + ) celery.conf.update(app.config) - TaskBase = celery.Task - class ContextTask(TaskBase): - abstract = True + + class ContextTask(celery.Task): def __call__(self, *args, **kwargs): with app.app_context(): return self.run(*args, **kwargs) + celery.Task = ContextTask return celery @@ -53,11 +59,12 @@ from the application config, updates the rest of the Celery config from the Flask config and then creates a subclass of the task that wraps the task execution in an application context. -Minimal Example +An example task --------------- -With what we have above this is the minimal example of using Celery with -Flask:: +Let's write a task that adds two numbers together and returns the result. We +configure Celery's broker and backend to use Redis, create a ``celery`` +application using the factor from above, and then use it to define the task. :: from flask import Flask @@ -68,26 +75,27 @@ Flask:: ) celery = make_celery(flask_app) - @celery.task() def add_together(a, b): return a + b -This task can now be called in the background: +This task can now be called in the background:: ->>> result = add_together.delay(23, 42) ->>> result.wait() -65 + result = add_together.delay(23, 42) + result.wait() # 65 -Running the Celery Worker -------------------------- +Run a worker +------------ -Now if you jumped in and already executed the above code you will be -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:: +If you jumped in and already executed the above code you will be +disappointed to learn that ``.wait()`` will never actually return. +That's because you also need to run a Celery worker to receive and execute the +task. :: $ 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. +or module that creates the ``celery`` object. + +Now that the worker is running, ``wait`` will return the result once the task +is finished. From 2a6579430649d6514384ca592730c6995e140c43 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 15 May 2017 16:58:01 -0700 Subject: [PATCH 173/399] safe_join on Windows uses posixpath fixes #2033 closes #2059 --- flask/helpers.py | 22 ++++++++++++++-------- tests/test_helpers.py | 25 ++++++++++++------------- 2 files changed, 26 insertions(+), 21 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index 51c3b064..f37be677 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -638,18 +638,24 @@ def safe_join(directory, *pathnames): :raises: :class:`~werkzeug.exceptions.NotFound` if one or more passed paths fall out of its boundaries. """ + + parts = [directory] + for filename in pathnames: if filename != '': filename = posixpath.normpath(filename) - for sep in _os_alt_seps: - if sep in filename: - raise NotFound() - if os.path.isabs(filename) or \ - filename == '..' or \ - filename.startswith('../'): + + if ( + any(sep in filename for sep in _os_alt_seps) + or os.path.isabs(filename) + or filename == '..' + or filename.startswith('../') + ): raise NotFound() - directory = os.path.join(directory, filename) - return directory + + parts.append(filename) + + return posixpath.join(*parts) def send_from_directory(directory, filename, **options): diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 325713c0..d5706521 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -903,21 +903,20 @@ class TestStreaming(object): class TestSafeJoin(object): - def test_safe_join(self): # Valid combinations of *args and expected joined paths. passing = ( - (('a/b/c', ), 'a/b/c'), - (('/', 'a/', 'b/', 'c/', ), '/a/b/c'), - (('a', 'b', 'c', ), 'a/b/c'), - (('/a', 'b/c', ), '/a/b/c'), - (('a/b', 'X/../c'), 'a/b/c', ), - (('/a/b', 'c/X/..'), '/a/b/c', ), + (('a/b/c',), 'a/b/c'), + (('/', 'a/', 'b/', 'c/'), '/a/b/c'), + (('a', 'b', 'c'), 'a/b/c'), + (('/a', 'b/c'), '/a/b/c'), + (('a/b', 'X/../c'), 'a/b/c'), + (('/a/b', 'c/X/..'), '/a/b/c'), # If last path is '' add a slash - (('/a/b/c', '', ), '/a/b/c/', ), + (('/a/b/c', ''), '/a/b/c/'), # Preserve dot slash - (('/a/b/c', './', ), '/a/b/c/.', ), - (('a/b/c', 'X/..'), 'a/b/c/.', ), + (('/a/b/c', './'), '/a/b/c/.'), + (('a/b/c', 'X/..'), 'a/b/c/.'), # Base directory is always considered safe (('../', 'a/b/c'), '../a/b/c'), (('/..', ), '/..'), @@ -931,12 +930,12 @@ class TestSafeJoin(object): failing = ( # path.isabs and ``..'' checks ('/a', 'b', '/c'), - ('/a', '../b/c', ), + ('/a', '../b/c'), ('/a', '..', 'b/c'), # Boundaries violations after path normalization - ('/a', 'b/../b/../../c', ), + ('/a', 'b/../b/../../c'), ('/a', 'b', 'c/../..'), - ('/a', 'b/../../c', ), + ('/a', 'b/../../c'), ) for args in failing: From f7c35bf0d51d1ae02709e39fe29110e12f64fb87 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 15 May 2017 16:58:01 -0700 Subject: [PATCH 174/399] safe_join on Windows uses posixpath fixes #2033 closes #2059 --- CHANGES | 7 +++++++ flask/helpers.py | 22 ++++++++++++++-------- tests/test_helpers.py | 25 ++++++++++++------------- 3 files changed, 33 insertions(+), 21 deletions(-) diff --git a/CHANGES b/CHANGES index 613b8189..a67c8bf9 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,13 @@ Major release, unreleased method returns compressed response by default, and pretty response in debug mode. +Version 0.12.2 +-------------- + +Bugfix release + +- Fix a bug in `safe_join` on Windows. + Version 0.12.1 -------------- diff --git a/flask/helpers.py b/flask/helpers.py index c6c2cddc..4bb1d1c9 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -619,18 +619,24 @@ def safe_join(directory, *pathnames): :raises: :class:`~werkzeug.exceptions.NotFound` if one or more passed paths fall out of its boundaries. """ + + parts = [directory] + for filename in pathnames: if filename != '': filename = posixpath.normpath(filename) - for sep in _os_alt_seps: - if sep in filename: - raise NotFound() - if os.path.isabs(filename) or \ - filename == '..' or \ - filename.startswith('../'): + + if ( + any(sep in filename for sep in _os_alt_seps) + or os.path.isabs(filename) + or filename == '..' + or filename.startswith('../') + ): raise NotFound() - directory = os.path.join(directory, filename) - return directory + + parts.append(filename) + + return posixpath.join(*parts) def send_from_directory(directory, filename, **options): diff --git a/tests/test_helpers.py b/tests/test_helpers.py index b1241ce9..9320ef71 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -846,21 +846,20 @@ class TestStreaming(object): class TestSafeJoin(object): - def test_safe_join(self): # Valid combinations of *args and expected joined paths. passing = ( - (('a/b/c', ), 'a/b/c'), - (('/', 'a/', 'b/', 'c/', ), '/a/b/c'), - (('a', 'b', 'c', ), 'a/b/c'), - (('/a', 'b/c', ), '/a/b/c'), - (('a/b', 'X/../c'), 'a/b/c', ), - (('/a/b', 'c/X/..'), '/a/b/c', ), + (('a/b/c',), 'a/b/c'), + (('/', 'a/', 'b/', 'c/'), '/a/b/c'), + (('a', 'b', 'c'), 'a/b/c'), + (('/a', 'b/c'), '/a/b/c'), + (('a/b', 'X/../c'), 'a/b/c'), + (('/a/b', 'c/X/..'), '/a/b/c'), # If last path is '' add a slash - (('/a/b/c', '', ), '/a/b/c/', ), + (('/a/b/c', ''), '/a/b/c/'), # Preserve dot slash - (('/a/b/c', './', ), '/a/b/c/.', ), - (('a/b/c', 'X/..'), 'a/b/c/.', ), + (('/a/b/c', './'), '/a/b/c/.'), + (('a/b/c', 'X/..'), 'a/b/c/.'), # Base directory is always considered safe (('../', 'a/b/c'), '../a/b/c'), (('/..', ), '/..'), @@ -874,12 +873,12 @@ class TestSafeJoin(object): failing = ( # path.isabs and ``..'' checks ('/a', 'b', '/c'), - ('/a', '../b/c', ), + ('/a', '../b/c'), ('/a', '..', 'b/c'), # Boundaries violations after path normalization - ('/a', 'b/../b/../../c', ), + ('/a', 'b/../b/../../c'), ('/a', 'b', 'c/../..'), - ('/a', 'b/../../c', ), + ('/a', 'b/../../c'), ) for args in failing: From bb83ae9843fd1e170a97139d489399c4823ba779 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Tue, 16 May 2017 08:39:28 +0200 Subject: [PATCH 175/399] Release 0.12.2 --- CHANGES | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index a67c8bf9..3456276a 100644 --- a/CHANGES +++ b/CHANGES @@ -18,7 +18,7 @@ Major release, unreleased Version 0.12.2 -------------- -Bugfix release +Released on May 16 2017 - Fix a bug in `safe_join` on Windows. From 571334df8e26333f34873a3dcb84441946e6c64c Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Tue, 16 May 2017 08:39:30 +0200 Subject: [PATCH 176/399] Bump version number to 0.12.2 --- flask/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__init__.py b/flask/__init__.py index 59d711b8..a9a873fa 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.12.2-dev' +__version__ = '0.12.2' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. From f347d3c59e3304474ca85b17e24261f127b27282 Mon Sep 17 00:00:00 2001 From: Markus Unterwaditzer Date: Tue, 16 May 2017 08:40:08 +0200 Subject: [PATCH 177/399] Bump to dev version --- flask/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__init__.py b/flask/__init__.py index a9a873fa..28a87cb6 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -10,7 +10,7 @@ :license: BSD, see LICENSE for more details. """ -__version__ = '0.12.2' +__version__ = '0.12.3-dev' # utilities we import from Werkzeug and Jinja2 that are unused # in the module but are exported as public interface. From ae133aa1734775f03560bde3d98c7f7d1c1569f7 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 20 May 2017 12:11:37 -0700 Subject: [PATCH 178/399] reorder session cookie checks to deleted, accessed, modified --- flask/sessions.py | 75 +++++++++++++++++++++++------------------------ 1 file changed, 37 insertions(+), 38 deletions(-) diff --git a/flask/sessions.py b/flask/sessions.py index 2eb887fc..182ace85 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -125,7 +125,8 @@ class SecureCookieSession(CallbackDict, SessionMixin): def on_update(self): self.modified = True self.accessed = True - CallbackDict.__init__(self, initial, on_update) + + super(SecureCookieSession, self).__init__(initial, on_update) self.modified = False self.accessed = False @@ -306,22 +307,20 @@ class SessionInterface(object): return datetime.utcnow() + app.permanent_session_lifetime def should_set_cookie(self, app, session): - """Indicates whether 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. + """Used by session backends to determine if a ``Set-Cookie`` header + should be set for this session cookie for this response. If the session + has been modified, the cookie is set. If the session is permanent and + the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is + always set. + + This check is usually skipped if the session was deleted. .. versionadded:: 0.11 """ - if session.modified: - return True - save_each = app.config['SESSION_REFRESH_EACH_REQUEST'] - return save_each and session.permanent + + return session.modified or ( + session.permanent and app.config['SESSION_REFRESH_EACH_REQUEST'] + ) def open_session(self, app, request): """This method has to be implemented and must either return ``None`` @@ -387,35 +386,35 @@ class SecureCookieSessionInterface(SessionInterface): domain = self.get_cookie_domain(app) path = self.get_cookie_path(app) - if session.accessed: + # If the session is modified to be empty, remove the cookie. + # If the session is empty, return without setting the cookie. + if not session: + if session.modified: + response.delete_cookie( + app.session_cookie_name, + domain=domain, + path=path + ) + + return + # Add a "Vary: Cookie" header if the session was accessed at all. + if session.accessed: 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 + 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) val = self.get_signing_serializer(app).dumps(dict(session)) - response.set_cookie(app.session_cookie_name, val, - expires=expires, httponly=httponly, - domain=domain, path=path, secure=secure) + response.set_cookie( + app.session_cookie_name, + val, + expires=expires, + httponly=httponly, + domain=domain, + path=path, + secure=secure + ) From 5d9dd0b379a63d5a90265f2469f86fbd81b05853 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 20 May 2017 13:00:17 -0700 Subject: [PATCH 179/399] set session accessed for setdefault --- flask/sessions.py | 5 +++++ tests/test_basic.py | 40 ++++++++++++++++++++++------------------ 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/flask/sessions.py b/flask/sessions.py index 182ace85..2dbd8b32 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -138,6 +138,11 @@ class SecureCookieSession(CallbackDict, SessionMixin): self.accessed = True return super(SecureCookieSession, self).get(key, default) + def setdefault(self, key, default=None): + self.accessed = True + return super(SecureCookieSession, self).setdefault(key, default) + + class NullSession(SecureCookieSession): """Class used to generate nicer error messages if sessions are not available. Will still allow read-only access to the empty session diff --git a/tests/test_basic.py b/tests/test_basic.py index dd9fd17e..54f4c8e6 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -533,20 +533,22 @@ def test_session_vary_cookie(): app = flask.Flask(__name__) app.secret_key = 'testkey' - @app.route('/set-session') + @app.route('/set') def set_session(): flask.session['test'] = 'test' return '' - @app.route('/get-session') - def get_session(): - s = flask.session.get('test') - return '' + @app.route('/get') + def get(): + return flask.session.get('test') - @app.route('/get-session-with-dictionary') - def get_session_with_dictionary(): - s = flask.session['test'] - return '' + @app.route('/getitem') + def getitem(): + return flask.session['test'] + + @app.route('/setdefault') + def setdefault(): + return flask.session.setdefault('test', 'default') @app.route('/no-vary-header') def no_vary_header(): @@ -554,17 +556,19 @@ def test_session_vary_cookie(): c = app.test_client() - rv = c.get('/set-session') - assert rv.headers['Vary'] == 'Cookie' - - rv = c.get('/get-session') - assert rv.headers['Vary'] == 'Cookie' + def expect(path, header=True): + rv = c.get(path) - rv = c.get('/get-session-with-dictionary') - assert rv.headers['Vary'] == 'Cookie' + if header: + assert rv.headers['Vary'] == 'Cookie' + else: + assert 'Vary' not in rv.headers - rv = c.get('/no-vary-header') - assert 'Vary' not in rv.headers + expect('/set') + expect('/get') + expect('/getitem') + expect('/setdefault') + expect('/no-vary-header', False) def test_flashes(): From c590e820aadb9134a7fb19fe32c8ddae37199ec3 Mon Sep 17 00:00:00 2001 From: Tully Rankin Date: Mon, 22 May 2017 11:25:02 -0700 Subject: [PATCH 180/399] Updated documentation. Replaced term development mode with debug mode. #2261 --- docs/reqcontext.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/reqcontext.rst b/docs/reqcontext.rst index 51cd66f6..29af2677 100644 --- a/docs/reqcontext.rst +++ b/docs/reqcontext.rst @@ -119,9 +119,9 @@ understand what is actually happening. The new behavior is quite simple: not executed yet or at all (for example in test environments sometimes you might want to not execute before-request callbacks). -Now what happens on errors? In production mode if an exception is not -caught, the 500 internal server handler is called. In development mode -however the exception is not further processed and bubbles up to the WSGI +Now what happens on errors? If you are not in debug mode if an exception is not +caught, the 500 internal server handler is called. In debug mode +however the exception is not further processed and bubbles up to the WSGI server. That way things like the interactive debugger can provide helpful debug information. @@ -214,10 +214,11 @@ provide you with important information. Starting with Flask 0.7 you have finer control over that behavior by setting the ``PRESERVE_CONTEXT_ON_EXCEPTION`` configuration variable. By default it's linked to the setting of ``DEBUG``. If the application is in -debug mode the context is preserved, in production mode it's not. +debug mode the context is preserved. If debug mode is set to off, the context +is not preserved. -Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` in production mode -as it will cause your application to leak memory on exceptions. However +Do not force activate ``PRESERVE_CONTEXT_ON_EXCEPTION`` if debug mode is set to off +as it will cause your application to leak memory on exceptions. However, it can be useful during development to get the same error preserving -behavior as in development mode when attempting to debug an error that +behavior as debug mode when attempting to debug an error that only occurs under production settings. From 041c68f48baa2ee3a8437c5f0e8c6fbd6039f28f Mon Sep 17 00:00:00 2001 From: Tully Rankin Date: Mon, 22 May 2017 11:28:58 -0700 Subject: [PATCH 181/399] Updated request context documentation. --- docs/reqcontext.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/reqcontext.rst b/docs/reqcontext.rst index 29af2677..c3d37297 100644 --- a/docs/reqcontext.rst +++ b/docs/reqcontext.rst @@ -119,7 +119,7 @@ understand what is actually happening. The new behavior is quite simple: not executed yet or at all (for example in test environments sometimes you might want to not execute before-request callbacks). -Now what happens on errors? If you are not in debug mode if an exception is not +Now what happens on errors? If you are not in debug mode and an exception is not caught, the 500 internal server handler is called. In debug mode however the exception is not further processed and bubbles up to the WSGI server. That way things like the interactive debugger can provide helpful From 9fddecd4d92763f38a42a3842abab70b0a6f4678 Mon Sep 17 00:00:00 2001 From: Randy Liou Date: Mon, 22 May 2017 11:55:23 -0700 Subject: [PATCH 182/399] Add coverage for Blueprint.app_errorhandler This test case registers an application-wise error handler from a Blueprint. Verifies the error handler by aborting the flask app from the application itself as well as from another registered Blueprint. --- tests/test_blueprints.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index de293e7f..e48fddc0 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -91,6 +91,33 @@ def test_blueprint_specific_user_error_handling(): assert c.get('/decorator').data == b'boom' assert c.get('/function').data == b'bam' +def test_blueprint_app_error_handling(): + errors = flask.Blueprint('errors', __name__) + + @errors.app_errorhandler(403) + def forbidden_handler(e): + return 'you shall not pass', 403 + + app = flask.Flask(__name__) + + @app.route('/forbidden') + def app_forbidden(): + flask.abort(403) + + forbidden_bp = flask.Blueprint('forbidden_bp', __name__) + + @forbidden_bp.route('/nope') + def bp_forbidden(): + flask.abort(403) + + app.register_blueprint(errors) + app.register_blueprint(forbidden_bp) + + c = app.test_client() + + assert c.get('/forbidden').data == b'you shall not pass' + assert c.get('/nope').data == b'you shall not pass' + def test_blueprint_url_definitions(): bp = flask.Blueprint('test', __name__) From 409dd15c10cbf79a52af554fd26a6e9c07ccd452 Mon Sep 17 00:00:00 2001 From: Tully Rankin Date: Mon, 22 May 2017 12:14:52 -0700 Subject: [PATCH 183/399] Added link to using Werkzeug debugger in quickstart documentation. --- docs/quickstart.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7bdb67e4..6a18053e 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -153,6 +153,11 @@ Screenshot of the debugger in action: :class: screenshot :alt: screenshot of debugger in action +More information on using the debugger can be found in the `Werkzeug +documentation`_. + +.. _Werkzeug documentation: http://werkzeug.pocoo.org/docs/0.11/debug/#using-the-debugger + Have another debugger in mind? See :ref:`working-with-debuggers`. From 50b73f967bae6b452220155de830a2c0c24bfd2e Mon Sep 17 00:00:00 2001 From: Tully Rankin Date: Mon, 22 May 2017 12:19:52 -0700 Subject: [PATCH 184/399] Removed the version number out of the documenation link to Werkzeug. --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 6a18053e..aea776f3 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -156,7 +156,7 @@ Screenshot of the debugger in action: More information on using the debugger can be found in the `Werkzeug documentation`_. -.. _Werkzeug documentation: http://werkzeug.pocoo.org/docs/0.11/debug/#using-the-debugger +.. _Werkzeug documentation: http://werkzeug.pocoo.org/docs/debug/#using-the-debugger Have another debugger in mind? See :ref:`working-with-debuggers`. From ced719ea18a56f6c4075e08dbe05f0e77eac1866 Mon Sep 17 00:00:00 2001 From: Hendrik Makait Date: Mon, 22 May 2017 12:30:18 -0700 Subject: [PATCH 185/399] Auto-detect create_app and make_app factory functions --- flask/cli.py | 15 +++++++++++++++ tests/test_cli.py | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/flask/cli.py b/flask/cli.py index 3d361be8..cdb7f094 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -46,6 +46,21 @@ def find_best_app(module): if len(matches) == 1: return matches[0] + + # Search for app factory callables. + for attr_name in 'create_app', 'make_app': + app_factory = getattr(module, attr_name, None) + if app_factory is not None and callable(app_factory): + try: + app = app_factory() + if app is not None and isinstance(app, Flask): + return app + except TypeError: + raise NoAppException('Auto-detected "%s()" in module "%s", ' + 'but could not call it without ' + 'specifying arguments.' + % (attr_name, module.__name__)) + 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 ' diff --git a/tests/test_cli.py b/tests/test_cli.py index ab875cef..bbb6fe58 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -51,6 +51,34 @@ def test_find_best_app(test_apps): myapp = Flask('appname') assert find_best_app(Module) == Module.myapp + class Module: + @staticmethod + def create_app(): + return Flask('appname') + assert isinstance(find_best_app(Module), Flask) + assert find_best_app(Module).name == 'appname' + + class Module: + @staticmethod + def make_app(): + return Flask('appname') + assert isinstance(find_best_app(Module), Flask) + assert find_best_app(Module).name == 'appname' + + class Module: + myapp = Flask('appname1') + @staticmethod + def create_app(): + return Flask('appname2') + assert find_best_app(Module) == Module.myapp + + class Module: + myapp = Flask('appname1') + @staticmethod + def create_app(foo): + return Flask('appname2') + assert find_best_app(Module) == Module.myapp + class Module: pass pytest.raises(NoAppException, find_best_app, Module) @@ -60,6 +88,12 @@ def test_find_best_app(test_apps): myapp2 = Flask('appname2') pytest.raises(NoAppException, find_best_app, Module) + class Module: + @staticmethod + def create_app(foo): + return Flask('appname2') + pytest.raises(NoAppException, find_best_app, Module) + def test_prepare_exec_for_file(test_apps): """Expect the correct path to be set and the correct module name to be returned. From 136dbf7de014dda29f3af89d1ba4fd4f4620e25c Mon Sep 17 00:00:00 2001 From: Randy Liou Date: Mon, 22 May 2017 13:06:47 -0700 Subject: [PATCH 186/399] Add coverage for Blueprints.(app_)context_processor Test both context_processor and app_context_processor functions. Two context parameters are added into the context: one added to the blueprint locally; another added to the app globally. The test asserts the behaviors in both blueprint scope and the app scope. The coverage for flask.blueprints is increased by 3%. --- tests/test_blueprints.py | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index de293e7f..cee84d4e 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -591,3 +591,45 @@ def test_add_template_test_with_name_and_template(): return flask.render_template('template_test.html', value=False) rv = app.test_client().get('/') assert b'Success!' in rv.data + +def test_context_processing(): + app = flask.Flask(__name__) + answer_bp = flask.Blueprint('answer_bp', __name__) + + template_string = lambda: flask.render_template_string( + '{% if notanswer %}{{ notanswer }} is not the answer. {% endif %}' + '{% if answer %}{{ answer }} is the answer.{% endif %}' + ) + + # App global context processor + @answer_bp.app_context_processor + def not_answer_context_processor(): + return {'notanswer': 43} + + # Blueprint local context processor + @answer_bp.context_processor + def answer_context_processor(): + return {'answer': 42} + + # Setup endpoints for testing + @answer_bp.route('/bp') + def bp_page(): + return template_string() + + @app.route('/') + def app_page(): + return template_string() + + # Register the blueprint + app.register_blueprint(answer_bp) + + c = app.test_client() + + app_page_bytes = c.get('/').data + answer_page_bytes = c.get('/bp').data + + assert b'43' in app_page_bytes + assert b'42' not in app_page_bytes + + assert b'42' in answer_page_bytes + assert b'43' in answer_page_bytes From b4eb6534d52e86af3979b06734e5779f6bb9a4f6 Mon Sep 17 00:00:00 2001 From: Hendrik Makait Date: Mon, 22 May 2017 14:26:00 -0700 Subject: [PATCH 187/399] Remove unnecessary checks and reformat NoAppException messages --- flask/cli.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index cdb7f094..109ba3fe 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -37,7 +37,7 @@ def find_best_app(module): # 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): + if isinstance(app, Flask): return app # Otherwise find the only object that is a Flask instance. @@ -50,21 +50,23 @@ def find_best_app(module): # Search for app factory callables. for attr_name in 'create_app', 'make_app': app_factory = getattr(module, attr_name, None) - if app_factory is not None and callable(app_factory): + if callable(app_factory): try: app = app_factory() - if app is not None and isinstance(app, Flask): + if isinstance(app, Flask): return app except TypeError: - raise NoAppException('Auto-detected "%s()" in module "%s", ' - 'but could not call it without ' - 'specifying arguments.' - % (attr_name, module.__name__)) - - 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__) + raise NoAppException( + 'Auto-detected "{callable}()" in module "{module}", but ' + 'could not call it without specifying arguments.' + .format(callable=attr_name, + module=module.__name__)) + + raise NoAppException( + 'Failed to find application in module "{module}". Are you sure ' + 'it contains a Flask application? Maybe you wrapped it in a WSGI ' + 'middleware or you are using a factory function.' + .format(module=module.__name__)) def prepare_exec_for_file(filename): From 7ce01ab9b4aac9e559cd6cb2e309381cfc3cc71b Mon Sep 17 00:00:00 2001 From: Randy Liou Date: Mon, 22 May 2017 14:14:24 -0700 Subject: [PATCH 188/399] Add coverage for Blueprint.add_app_template_global This tests the Blueprint.add_app_template_global mothod, which internally calls the Blueprint.app_template_global method. The methods are used to registering a function to the jinja template environment. This PR increases the test coverage for module flask.blueprint by 4%. --- tests/test_blueprints.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 12b58417..5f0d87da 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -660,3 +660,22 @@ def test_context_processing(): assert b'42' in answer_page_bytes assert b'43' in answer_page_bytes + +def test_template_global(): + app = flask.Flask(__name__) + bp = flask.Blueprint('bp', __name__) + @bp.app_template_global() + def get_answer(): + return 42 + # Make sure the function is not in the jinja_env already + assert 'get_answer' not in app.jinja_env.globals.keys() + app.register_blueprint(bp) + + # Tests + assert 'get_answer' in app.jinja_env.globals.keys() + assert app.jinja_env.globals['get_answer'] is get_answer + assert app.jinja_env.globals['get_answer']() == 42 + + with app.app_context(): + rv = flask.render_template_string('{{ get_answer() }}') + assert rv == '42' From d7d21f55596b141ae6ca3cc1a52e9b5be9d474eb Mon Sep 17 00:00:00 2001 From: Tully Rankin Date: Mon, 22 May 2017 14:48:24 -0700 Subject: [PATCH 189/399] Moved WSGI-Standalone above mod_wsgi in deployment documentation. --- docs/deploying/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst index 6950e47a..0bc61fc2 100644 --- a/docs/deploying/index.rst +++ b/docs/deploying/index.rst @@ -32,8 +32,8 @@ Self-hosted options .. toctree:: :maxdepth: 2 - mod_wsgi wsgi-standalone + mod_wsgi uwsgi fastcgi cgi From 2b96052240ff1113bfb0c376aaf7aa376785184d Mon Sep 17 00:00:00 2001 From: Tully Rankin Date: Mon, 22 May 2017 15:13:31 -0700 Subject: [PATCH 190/399] Moved mod_wsgi under uwsgi in TOC deployment docs. --- docs/deploying/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst index 0bc61fc2..da8ac14e 100644 --- a/docs/deploying/index.rst +++ b/docs/deploying/index.rst @@ -33,7 +33,7 @@ Self-hosted options :maxdepth: 2 wsgi-standalone - mod_wsgi uwsgi + mod_wsgi fastcgi cgi From 7ecdbcfa2b2d1c83c8c6561b49303ed5b1042350 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 22 May 2017 15:48:08 -0700 Subject: [PATCH 191/399] show error if multiple Flask instances are detected add changelog --- CHANGES | 3 +++ flask/cli.py | 28 +++++++++++++++++++--------- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 76a0d7a2..7effdfac 100644 --- a/CHANGES +++ b/CHANGES @@ -40,6 +40,8 @@ Major release, unreleased - Allow IP address as exact session cookie domain. (`#2282`_) - ``SESSION_COOKIE_DOMAIN`` is set if it is detected through ``SERVER_NAME``. (`#2282`_) +- Auto-detect 0-argument app factory called ``create_app`` or ``make_app`` from + ``FLASK_APP``. (`#2297`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1898: https://github.com/pallets/flask/pull/1898 @@ -50,6 +52,7 @@ Major release, unreleased .. _#2256: https://github.com/pallets/flask/pull/2256 .. _#2259: https://github.com/pallets/flask/pull/2259 .. _#2282: https://github.com/pallets/flask/pull/2282 +.. _#2297: https://github.com/pallets/flask/pull/2297 Version 0.12.2 -------------- diff --git a/flask/cli.py b/flask/cli.py index 109ba3fe..a10cf5e5 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -41,32 +41,42 @@ def find_best_app(module): 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)] + matches = [ + v for k, v in iteritems(module.__dict__) if isinstance(v, Flask) + ] if len(matches) == 1: return matches[0] + elif len(matches) > 1: + raise NoAppException( + 'Auto-detected multiple Flask applications in module "{module}".' + ' Use "FLASK_APP={module}:name" to specify the correct' + ' one.'.format(module=module.__name__) + ) # Search for app factory callables. for attr_name in 'create_app', 'make_app': app_factory = getattr(module, attr_name, None) + if callable(app_factory): try: app = app_factory() + if isinstance(app, Flask): return app except TypeError: raise NoAppException( 'Auto-detected "{callable}()" in module "{module}", but ' - 'could not call it without specifying arguments.' - .format(callable=attr_name, - module=module.__name__)) + 'could not call it without specifying arguments.'.format( + callable=attr_name, module=module.__name__ + ) + ) raise NoAppException( - 'Failed to find application in module "{module}". Are you sure ' - 'it contains a Flask application? Maybe you wrapped it in a WSGI ' - 'middleware or you are using a factory function.' - .format(module=module.__name__)) + 'Failed to find application in module "{module}". Are you sure ' + 'it contains a Flask application? Maybe you wrapped it in a WSGI ' + 'middleware.'.format(module=module.__name__) + ) def prepare_exec_for_file(filename): From 01ddf54b87850c50ee9020c6027647e0c5fca283 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 22 May 2017 16:12:23 -0700 Subject: [PATCH 192/399] adjust for loop style --- CHANGES | 4 ++-- flask/cli.py | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index 7effdfac..d243d66d 100644 --- a/CHANGES +++ b/CHANGES @@ -40,8 +40,8 @@ Major release, unreleased - Allow IP address as exact session cookie domain. (`#2282`_) - ``SESSION_COOKIE_DOMAIN`` is set if it is detected through ``SERVER_NAME``. (`#2282`_) -- Auto-detect 0-argument app factory called ``create_app`` or ``make_app`` from - ``FLASK_APP``. (`#2297`_) +- Auto-detect zero-argument app factory called ``create_app`` or ``make_app`` + from ``FLASK_APP``. (`#2297`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1898: https://github.com/pallets/flask/pull/1898 diff --git a/flask/cli.py b/flask/cli.py index a10cf5e5..6aa66f4f 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -35,7 +35,7 @@ def find_best_app(module): from . import Flask # Search for the most common names first. - for attr_name in 'app', 'application': + for attr_name in ('app', 'application'): app = getattr(module, attr_name, None) if isinstance(app, Flask): return app @@ -55,7 +55,7 @@ def find_best_app(module): ) # Search for app factory callables. - for attr_name in 'create_app', 'make_app': + for attr_name in ('create_app', 'make_app'): app_factory = getattr(module, attr_name, None) if callable(app_factory): From 6f49089a624a13353a07db87aff5fe6f01699728 Mon Sep 17 00:00:00 2001 From: MikeTheReader Date: Mon, 22 May 2017 16:15:48 -0700 Subject: [PATCH 193/399] Added tests for make_response and get_debug_flag to improve coverage of helpers.py --- tests/test_helpers.py | 50 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index d5706521..19c364f5 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -22,6 +22,7 @@ from werkzeug.exceptions import BadRequest, NotFound from werkzeug.http import parse_cache_control_header, parse_options_header from werkzeug.http import http_date from flask._compat import StringIO, text_type +from flask.helpers import get_debug_flag, make_response def has_encoding(name): @@ -941,3 +942,52 @@ class TestSafeJoin(object): for args in failing: with pytest.raises(NotFound): print(flask.safe_join(*args)) + +class TestHelpers(object): + + def test_get_debug_flag(self): + original_debug_value = os.environ.get('FLASK_DEBUG') or '' + os.environ['FLASK_DEBUG'] = '' + assert get_debug_flag() == None + assert get_debug_flag(default=True) == True + + os.environ['FLASK_DEBUG'] = '0' + assert get_debug_flag() == False + assert get_debug_flag(default=True) == False + + os.environ['FLASK_DEBUG'] = 'False' + assert get_debug_flag() == False + assert get_debug_flag(default=True) == False + + os.environ['FLASK_DEBUG'] = 'No' + assert get_debug_flag() == False + assert get_debug_flag(default=True) == False + + os.environ['FLASK_DEBUG'] = 'True' + assert get_debug_flag() == True + assert get_debug_flag(default=True) == True + + os.environ['FLASK_DEBUG'] = original_debug_value + + def test_make_response_no_args(self): + app = flask.Flask(__name__) + app.testing = True + @app.route('/') + def index(): + return flask.helpers.make_response() + c = app.test_client() + rv = c.get() + assert rv + + def test_make_response_with_args(self): + app = flask.Flask(__name__) + app.testing = True + @app.route('/') + def index(): + response = flask.helpers.make_response(flask.render_template_string('Hello World')) + response.headers['X-Parachutes'] = 'parachutes are cool' + return response + c = app.test_client() + rv = c.get() + assert rv + assert rv.headers['X-Parachutes'] == 'parachutes are cool' From a690ae27a392cfa1474e062f7655250c416cd536 Mon Sep 17 00:00:00 2001 From: Randy Liou Date: Mon, 22 May 2017 15:49:04 -0700 Subject: [PATCH 194/399] Add coverage for Blueprint request process methods Add test to cover following methodss to the Blueprint object: before_request, after_request, before_app_request, before_app_first_request, after_app_request. This PR increases the coverage of flask.blueprints by 6%. --- tests/test_blueprints.py | 62 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 5f0d87da..78da0d2c 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -679,3 +679,65 @@ def test_template_global(): with app.app_context(): rv = flask.render_template_string('{{ get_answer() }}') assert rv == '42' + +def test_request_processing(): + app = flask.Flask(__name__) + bp = flask.Blueprint('bp', __name__) + evts = [] + @bp.before_request + def before_bp(): + evts.append('before') + @bp.after_request + def after_bp(response): + response.data += b'|after' + evts.append('after') + return response + + # Setup routes for testing + @bp.route('/bp') + def bp_endpoint(): + return 'request' + + app.register_blueprint(bp) + + assert evts == [] + rv = app.test_client().get('/bp') + assert rv.data == b'request|after' + assert evts == ['before', 'after'] + +def test_app_request_processing(): + app = flask.Flask(__name__) + bp = flask.Blueprint('bp', __name__) + evts = [] + + @bp.before_app_first_request + def before_first_request(): + evts.append('first') + @bp.before_app_request + def before_app(): + evts.append('before') + @bp.after_app_request + def after_app(response): + response.data += b'|after' + evts.append('after') + return response + + app.register_blueprint(bp) + + # Setup routes for testing + @app.route('/') + def bp_endpoint(): + return 'request' + + # before first request + assert evts == [] + + # first request + resp = app.test_client().get('/').data + assert resp == b'request|after' + assert evts == ['first', 'before', 'after'] + + # second request + resp = app.test_client().get('/').data + assert resp == b'request|after' + assert evts == ['first', 'before', 'after', 'before', 'after'] From d8d712a0def83d146d155acb941c31781e7c3b3a Mon Sep 17 00:00:00 2001 From: Randy Liou Date: Mon, 22 May 2017 16:54:52 -0700 Subject: [PATCH 195/399] Add coverage for Blueprint teardown request method Test the following methods in the Blueprint object: teardown_request, and teardown_app_request. This PR increases the coverage of blueprint module by 3%. --- tests/test_blueprints.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 78da0d2c..5c5119c0 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -692,6 +692,9 @@ def test_request_processing(): response.data += b'|after' evts.append('after') return response + @bp.teardown_request + def teardown_bp(exc): + evts.append('teardown') # Setup routes for testing @bp.route('/bp') @@ -703,7 +706,7 @@ def test_request_processing(): assert evts == [] rv = app.test_client().get('/bp') assert rv.data == b'request|after' - assert evts == ['before', 'after'] + assert evts == ['before', 'after', 'teardown'] def test_app_request_processing(): app = flask.Flask(__name__) @@ -721,6 +724,9 @@ def test_app_request_processing(): response.data += b'|after' evts.append('after') return response + @bp.teardown_app_request + def teardown_app(exc): + evts.append('teardown') app.register_blueprint(bp) @@ -735,9 +741,9 @@ def test_app_request_processing(): # first request resp = app.test_client().get('/').data assert resp == b'request|after' - assert evts == ['first', 'before', 'after'] + assert evts == ['first', 'before', 'after', 'teardown'] # second request resp = app.test_client().get('/').data assert resp == b'request|after' - assert evts == ['first', 'before', 'after', 'before', 'after'] + assert evts == ['first'] + ['before', 'after', 'teardown'] * 2 From 65fc888172fbff89a8354e8926a69b4515766389 Mon Sep 17 00:00:00 2001 From: Neil Grey Date: Mon, 22 May 2017 17:36:55 -0700 Subject: [PATCH 196/399] For Issue #2286: Replaces references to unittest in the documentation with pytest --- docs/signals.rst | 4 +- docs/testing.rst | 200 ++++++++++++++++++++++++++--------------------- flask/app.py | 2 +- 3 files changed, 112 insertions(+), 94 deletions(-) diff --git a/docs/signals.rst b/docs/signals.rst index 2426e920..40041491 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -27,7 +27,7 @@ executed in undefined order and do not modify any data. The big advantage of signals over handlers is that you can safely subscribe to them for just a split second. These temporary -subscriptions are helpful for unittesting for example. Say you want to +subscriptions are helpful for unit testing for example. Say you want to know what templates were rendered as part of a request: signals allow you to do exactly that. @@ -45,7 +45,7 @@ signal. When you subscribe to a signal, be sure to also provide a sender unless you really want to listen for signals from all applications. This is especially true if you are developing an extension. -For example, here is a helper context manager that can be used in a unittest +For example, here is a helper context manager that can be used in a unit test to determine which templates were rendered and what variables were passed to the template:: diff --git a/docs/testing.rst b/docs/testing.rst index bd1c8467..eb0737da 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -5,23 +5,30 @@ Testing Flask Applications **Something that is untested is broken.** -The origin of this quote is unknown and while it is not entirely correct, it is also -not far from the truth. Untested applications make it hard to +The origin of this quote is unknown and while it is not entirely correct, it +is also not far from the truth. Untested applications make it hard to 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 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. +You can then use that with your favourite testing solution. + +In this documentation we will use the `pytest`_ package as the base +framework for our tests. You can install it with ``pip``, like so:: + + pip install pytest + +.. _pytest: + https://pytest.org 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 -sources from `the examples`_. +source code from `the examples`_. .. _the examples: https://github.com/pallets/flask/tree/master/examples/flaskr/ @@ -29,92 +36,89 @@ sources from `the examples`_. The Testing Skeleton -------------------- -In order to test the application, we add a second module -(:file:`flaskr_tests.py`) and create a unittest skeleton there:: +We begin by adding a tests directory under the application root. Then +create a Python file to store our tests (:file:`test_flaskr.py`). When we +format the filename like ``test_*.py``, it will be auto-discoverable by +pytest. + +Next, we create a `pytest fixture`_ called +:func:`client` that configures +the application for testing and initializes a new database.:: import os - from flaskr import flaskr - import unittest import tempfile + import pytest + from flaskr import flaskr - class FlaskrTestCase(unittest.TestCase): - - def setUp(self): - self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.testing = True - self.app = flaskr.app.test_client() - with flaskr.app.app_context(): - flaskr.init_db() + @pytest.fixture + def client(request): + db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() + flaskr.app.config['TESTING'] = True + client = flaskr.app.test_client() + with flaskr.app.app_context(): + flaskr.init_db() - def tearDown(self): - os.close(self.db_fd) + def teardown(): + os.close(db_fd) os.unlink(flaskr.app.config['DATABASE']) + request.addfinalizer(teardown) - if __name__ == '__main__': - unittest.main() + return client -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 -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 disable the error -catching during request handling so that you get better error reports when -performing test requests against the application. +This client fixture will be called by each individual test. It gives us a +simple interface to the application, where we can trigger test requests to the +application. The client will also keep track of cookies for us. -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. +During setup, the ``TESTING`` config flag is activated. What +this does is disable error catching during request handling, so that +you get better error reports when performing test requests against the +application. -Because SQLite3 is filesystem-based we can easily use the tempfile module +Because SQLite3 is filesystem-based, we can easily use the :mod:`tempfile` module to create a temporary database and initialize it. The :func:`~tempfile.mkstemp` function does two things for us: it returns a low-level file handle and a random file name, the latter we use as database name. We just have to keep the `db_fd` around so that we can use the :func:`os.close` function to close the file. +To delete the database after the test, we close the file and remove it +from the filesystem in the +:func:`teardown` function. + If we now run the test suite, we should see the following output:: - $ python flaskr_tests.py + $ pytest - ---------------------------------------------------------------------- - Ran 0 tests in 0.000s + ================ test session starts ================ + rootdir: ./flask/examples/flaskr, inifile: setup.cfg + collected 0 items - OK + =========== no tests ran in 0.07 seconds ============ -Even though it did not run any actual tests, we already know that our flaskr +Even though it did not run any actual tests, we already know that our ``flaskr`` application is syntactically valid, otherwise the import would have died with an exception. +.. _pytest fixture: + https://docs.pytest.org/en/latest/fixture.html + 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 -access the root of the application (``/``). To do this, we add a new -test method to our class, like this:: +access the root of the application (``/``). To do this, we add a new +test function to :file:`test_flaskr.py`, like this:: - class FlaskrTestCase(unittest.TestCase): - - def setUp(self): - self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() - flaskr.app.testing = True - self.app = flaskr.app.test_client() - with flaskr.app.app_context(): - flaskr.init_db() - - def tearDown(self): - os.close(self.db_fd) - os.unlink(flaskr.app.config['DATABASE']) - - def test_empty_db(self): - rv = self.app.get('/') - assert b'No entries here so far' in rv.data + def test_empty_db(client): + """Start with a blank database.""" + rv = client.get('/') + assert b'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. +`pytest`_ to automatically identify the function as a test to run. -By using `self.app.get` we can send an HTTP ``GET`` request to the application with +By using `client.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 @@ -122,12 +126,15 @@ the return value (as string) from the application. In this case, we ensure that Run it again and you should see one passing test:: - $ python flaskr_tests.py - . - ---------------------------------------------------------------------- - Ran 1 test in 0.034s + $ pytest -v + + ================ test session starts ================ + rootdir: ./flask/examples/flaskr, inifile: setup.cfg + collected 1 items - OK + tests/test_flaskr.py::test_empty_db PASSED + + ============= 1 passed in 0.10 seconds ============== Logging In and Out ------------------ @@ -138,39 +145,45 @@ 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:: +Add the following two functions to your :file:`test_flaskr.py` file:: - def login(self, username, password): - return self.app.post('/login', data=dict( - username=username, - password=password - ), follow_redirects=True) + def login(client, username, password): + return client.post('/login', data=dict( + username=username, + password=password + ), follow_redirects=True) - def logout(self): - return self.app.get('/logout', follow_redirects=True) + def logout(client): + return client.get('/logout', follow_redirects=True) Now we can easily test that logging in and out works and that it fails with -invalid credentials. Add this new test to the class:: - - def test_login_logout(self): - rv = self.login('admin', 'default') - assert b'You were logged in' in rv.data - rv = self.logout() - assert b'You were logged out' in rv.data - rv = self.login('adminx', 'default') - assert b'Invalid username' in rv.data - rv = self.login('admin', 'defaultx') - assert b'Invalid password' in rv.data +invalid credentials. Add this new test function:: + + def test_login_logout(client): + """Make sure login and logout works""" + rv = login(client, flaskr.app.config['USERNAME'], + flaskr.app.config['PASSWORD']) + assert b'You were logged in' in rv.data + rv = logout(client) + assert b'You were logged out' in rv.data + rv = login(client, flaskr.app.config['USERNAME'] + 'x', + flaskr.app.config['PASSWORD']) + assert b'Invalid username' in rv.data + rv = login(client, flaskr.app.config['USERNAME'], + flaskr.app.config['PASSWORD'] + 'x') + assert b'Invalid password' in rv.data Test Adding Messages -------------------- -We should also test that adding messages works. Add a new test method +We should also test that adding messages works. Add a new test function like this:: - def test_messages(self): - self.login('admin', 'default') - rv = self.app.post('/add', data=dict( + def test_messages(client): + """Test that messages work""" + login(client, flaskr.app.config['USERNAME'], + flaskr.app.config['PASSWORD']) + rv = client.post('/add', data=dict( title='', text='HTML allowed here' ), follow_redirects=True) @@ -183,12 +196,17 @@ which is the intended behavior. Running that should now give us three passing tests:: - $ python flaskr_tests.py - ... - ---------------------------------------------------------------------- - Ran 3 tests in 0.332s + $ pytest -v + + ================ test session starts ================ + rootdir: ./flask/examples/flaskr, inifile: setup.cfg + collected 3 items + + tests/test_flaskr.py::test_empty_db PASSED + tests/test_flaskr.py::test_login_logout PASSED + tests/test_flaskr.py::test_messages PASSED - OK + ============= 3 passed in 0.23 seconds ============== For more complex tests with headers and status codes, check out the `MiniTwit Example`_ from the sources which contains a larger test diff --git a/flask/app.py b/flask/app.py index 703cfef2..d851bf83 100644 --- a/flask/app.py +++ b/flask/app.py @@ -222,7 +222,7 @@ class Flask(_PackageBoundObject): #: The testing flag. Set this to ``True`` to enable the test mode of #: Flask extensions (and in the future probably also Flask itself). - #: For example this might activate unittest helpers that have an + #: For example this might activate test helpers that have an #: additional runtime cost which should not be enabled by default. #: #: If this is enabled and PROPAGATE_EXCEPTIONS is not changed from the From 4e9d51b39b674b5c1c00081c1b457ff03ba6e76c Mon Sep 17 00:00:00 2001 From: Neil Grey Date: Mon, 22 May 2017 17:42:30 -0700 Subject: [PATCH 197/399] For Issue #2286: Fixing indenting of test_login_logout --- docs/testing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/testing.rst b/docs/testing.rst index eb0737da..7a16e336 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -159,7 +159,7 @@ Add the following two functions to your :file:`test_flaskr.py` file:: Now we can easily test that logging in and out works and that it fails with invalid credentials. Add this new test function:: - def test_login_logout(client): + def test_login_logout(client): """Make sure login and logout works""" rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD']) From 65b61aa7c202bb26df53e2cb271269fddd95aeee Mon Sep 17 00:00:00 2001 From: Tully Rankin Date: Mon, 22 May 2017 18:08:40 -0700 Subject: [PATCH 198/399] Added uWSGI and example usage to stand-alone WSGI containers documentation (#2302) --- docs/deploying/wsgi-standalone.rst | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/docs/deploying/wsgi-standalone.rst b/docs/deploying/wsgi-standalone.rst index ad43c144..bf680976 100644 --- a/docs/deploying/wsgi-standalone.rst +++ b/docs/deploying/wsgi-standalone.rst @@ -27,6 +27,22 @@ For example, to run a Flask application with 4 worker processes (``-w .. _eventlet: http://eventlet.net/ .. _greenlet: https://greenlet.readthedocs.io/en/latest/ +uWSGI +-------- + +`uWSGI`_ is a fast application server written in C. It is very configurable +which makes it more complicated to setup than gunicorn. + +Running `uWSGI HTTP Router`_:: + + uwsgi --http 127.0.0.1:5000 --module myproject:app + +For a more optimized setup, see `configuring uWSGI and NGINX`_. + +.. _uWSGI: http://uwsgi-docs.readthedocs.io/en/latest/ +.. _uWSGI HTTP Router: http://uwsgi-docs.readthedocs.io/en/latest/HTTP.html#the-uwsgi-http-https-router +.. _configuring uWSGI and NGINX: uwsgi.html#starting-your-app-with-uwsgi + Gevent ------- From 378a11f99275a6b80d365a5d51d469844bf9ca17 Mon Sep 17 00:00:00 2001 From: Neil Grey Date: Mon, 22 May 2017 18:22:08 -0700 Subject: [PATCH 199/399] For Issue #2286: Updating test_flaskr to use yield inside fixture --- docs/testing.rst | 15 +++++---------- examples/flaskr/tests/test_flaskr.py | 10 +++------- 2 files changed, 8 insertions(+), 17 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 7a16e336..67b8aaea 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -57,13 +57,9 @@ the application for testing and initializes a new database.:: client = flaskr.app.test_client() with flaskr.app.app_context(): flaskr.init_db() - - def teardown(): - os.close(db_fd) - os.unlink(flaskr.app.config['DATABASE']) - request.addfinalizer(teardown) - - return client + yield client + os.close(db_fd) + os.unlink(flaskr.app.config['DATABASE']) This client fixture will be called by each individual test. It gives us a simple interface to the application, where we can trigger test requests to the @@ -82,8 +78,7 @@ database name. We just have to keep the `db_fd` around so that we can use the :func:`os.close` function to close the file. To delete the database after the test, we close the file and remove it -from the filesystem in the -:func:`teardown` function. +from the filesystem. If we now run the test suite, we should see the following output:: @@ -118,7 +113,7 @@ test function to :file:`test_flaskr.py`, like this:: Notice that our test functions begin with the word `test`; this allows `pytest`_ to automatically identify the function as a test to run. -By using `client.get` we can send an HTTP ``GET`` request to the application with +By using ``client.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 diff --git a/examples/flaskr/tests/test_flaskr.py b/examples/flaskr/tests/test_flaskr.py index 663e92e0..df32cd4b 100644 --- a/examples/flaskr/tests/test_flaskr.py +++ b/examples/flaskr/tests/test_flaskr.py @@ -22,13 +22,9 @@ def client(request): client = flaskr.app.test_client() with flaskr.app.app_context(): flaskr.init_db() - - def teardown(): - os.close(db_fd) - os.unlink(flaskr.app.config['DATABASE']) - request.addfinalizer(teardown) - - return client + yield client + os.close(db_fd) + os.unlink(flaskr.app.config['DATABASE']) def login(client, username, password): From fd4a363657f3b4f13611e029c52fcb8c806f7229 Mon Sep 17 00:00:00 2001 From: MikeTheReader Date: Mon, 22 May 2017 20:49:37 -0700 Subject: [PATCH 200/399] Modifications based on review --- tests/test_helpers.py | 44 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 29 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 19c364f5..a5863da9 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -945,49 +945,35 @@ class TestSafeJoin(object): class TestHelpers(object): - def test_get_debug_flag(self): - original_debug_value = os.environ.get('FLASK_DEBUG') or '' - os.environ['FLASK_DEBUG'] = '' + def test_get_debug_flag(self, monkeypatch): + monkeypatch.setenv('FLASK_DEBUG', '') assert get_debug_flag() == None assert get_debug_flag(default=True) == True - os.environ['FLASK_DEBUG'] = '0' + monkeypatch.setenv('FLASK_DEBUG', '0') assert get_debug_flag() == False assert get_debug_flag(default=True) == False - os.environ['FLASK_DEBUG'] = 'False' + monkeypatch.setenv('FLASK_DEBUG', 'False') assert get_debug_flag() == False assert get_debug_flag(default=True) == False - os.environ['FLASK_DEBUG'] = 'No' + monkeypatch.setenv('FLASK_DEBUG', 'No') assert get_debug_flag() == False assert get_debug_flag(default=True) == False - os.environ['FLASK_DEBUG'] = 'True' + monkeypatch.setenv('FLASK_DEBUG', 'True') assert get_debug_flag() == True assert get_debug_flag(default=True) == True - os.environ['FLASK_DEBUG'] = original_debug_value - - def test_make_response_no_args(self): + def test_make_response(self): app = flask.Flask(__name__) - app.testing = True - @app.route('/') - def index(): - return flask.helpers.make_response() - c = app.test_client() - rv = c.get() - assert rv + with app.test_request_context(): + rv = flask.helpers.make_response() + assert rv.status_code == 200 + assert rv.mimetype == 'text/html' - def test_make_response_with_args(self): - app = flask.Flask(__name__) - app.testing = True - @app.route('/') - def index(): - response = flask.helpers.make_response(flask.render_template_string('Hello World')) - response.headers['X-Parachutes'] = 'parachutes are cool' - return response - c = app.test_client() - rv = c.get() - assert rv - assert rv.headers['X-Parachutes'] == 'parachutes are cool' + rv = flask.helpers.make_response('Hello') + assert rv.status_code == 200 + assert rv.data == b'Hello' + assert rv.mimetype == 'text/html' From 8459cedaa94445fd17b223aed16312f5da2f76ac Mon Sep 17 00:00:00 2001 From: Lowell Abbott Date: Mon, 22 May 2017 20:52:02 -0700 Subject: [PATCH 201/399] Add security headers notes --- docs/security.rst | 127 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) diff --git a/docs/security.rst b/docs/security.rst index ad0d1244..914dd92a 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -104,3 +104,130 @@ vulnerabilities `_, so this behavior was changed and :func:`~flask.jsonify` now supports serializing arrays. + + +SSL/HTTPS +--------- + +For implementing HTTPS on your server + +Below some packages in suggestion order that implements this protocol: + +* `flask-talisman `_ +* `flask-sslify `_ +* `flask-secure-headers `_ + +Security Headers +---------------- + +This sections contains sections headers supported by Flask and a list of packages in suggestion order that implements it + +`Content Security Policy `_ (CSP) +----------------------------------------------------------------------------- + +For enhancing security and preventing common web vulnerabilities such as cross-site scripting and MITM related attacks + +Example + +.. sourcecode:: html + + Content-Security-Policy: default-src https:; script-src 'nonce-{random}'; object-src 'none' + + +To learn more check `this `_ + +* `flask-talisman `_ +* `flask-csp `_ +* `flask-secure-headers `_ + +`HTTP Strict Transport Security `_ (HSTS) +------------------------------------------------------------------------------------------------------------------------------ + + +For automatically redirect HTTP to HTTPS on all the website url's and prevent MITM attacks + +Example + +.. sourcecode:: html + + Strict-Transport-Security: max-age=; includeSubDomains + Strict-Transport-Security: max-age=; preload + +To learn more check `this `_ + + +* `flask-talisman `_ +* `flask-sslify `_ +* `flask-secure-headers `_ + +`X-FRAME-OPTIONS `_ (Clickjacking protection) +------------------------------------------------------------------------------------------------------------------------- +Prevents the client clicking page elements outside of the website avoiding hijacking or UI redress attacks + + +.. sourcecode:: html + + X-Frame-Options: DENY + X-Frame-Options: SAMEORIGIN + X-Frame-Options: ALLOW-FROM https://example.com/ + +To learn more check `this `_ + +* `flask-talisman `_ +* `flask-secure-headers `_ + +`X-Content-Type-Options `_ +------------------------------------------------------------------------------------------------------------- + +Prevents XSS by blocking requests on clients and forcing then to read the content type instead of first opening it. + +.. sourcecode:: html + + X-Content-Type-Options: nosniff + +To learn more check `this `_ + + +* `flask-talisman `_ +* `flask-secure-headers `_ + +`Cookie options `_ +---------------------------------------------------------------------------------------------------------- + +For setting cookies on client-side storage + +Example + +.. sourcecode:: html + + Set-Cookie: [cookie-name]=[cookie-value] + +To learn more check `this `_ + +* `flask-talisman `_ +* `flask-secure-headers `_ + +`HTTP Public Key Pinning `_ (HPKP) +------------------------------------------------------------------------------------------------------- + +For associating clients with web servers throught a certificate key and prevent MITM attacks + +Example + +.. sourcecode:: html + + Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"] + +To learn more check `this `_ + +* `flask-talisman `_ +* `flask-secure-headers `_ + +References: +----------- + +* https://docs.djangoproject.com/en/1.11/topics/security/ +* https://blog.appcanary.com/2017/http-security-headers.html +* https://developer.mozilla.org +* https://csp.withgoogle.com/docs/index.html From 11d2eec3acb7d0cf291b572e3b6b493df5fadc6b Mon Sep 17 00:00:00 2001 From: Andrey Kislyuk Date: Mon, 22 May 2017 23:46:22 -0700 Subject: [PATCH 202/399] Fix refactoring error in static_folder docstring (#2310) --- flask/app.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/flask/app.py b/flask/app.py index 703cfef2..13a56c68 100644 --- a/flask/app.py +++ b/flask/app.py @@ -133,8 +133,6 @@ class Flask(_PackageBoundObject): :param static_folder: the folder with static files that should be served at `static_url_path`. Defaults to the ``'static'`` folder in the root path of the application. - folder in the root path of the application. Defaults - to None. :param host_matching: sets the app's ``url_map.host_matching`` to the given given value. Defaults to False. :param static_host: the host to use when adding the static route. Defaults From 98b0f96a9820d6314cd814d27b25589ac08ccbf4 Mon Sep 17 00:00:00 2001 From: Lowell Abbott Date: Mon, 22 May 2017 23:48:35 -0700 Subject: [PATCH 203/399] Fix typos, semantics and some other corrections --- docs/security.rst | 43 ++++++++++++++++++++----------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/docs/security.rst b/docs/security.rst index 914dd92a..f2a1ee4e 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -109,9 +109,9 @@ arrays. SSL/HTTPS --------- -For implementing HTTPS on your server +For implementing HTTPS on your server. -Below some packages in suggestion order that implements this protocol: +Below are some packages that implement this protocol: * `flask-talisman `_ * `flask-sslify `_ @@ -120,21 +120,21 @@ Below some packages in suggestion order that implements this protocol: Security Headers ---------------- -This sections contains sections headers supported by Flask and a list of packages in suggestion order that implements it +This section contains a list of headers supported by Flask and some packages that implements them. `Content Security Policy `_ (CSP) ----------------------------------------------------------------------------- -For enhancing security and preventing common web vulnerabilities such as cross-site scripting and MITM related attacks +Enhance security and prevents common web vulnerabilities such as cross-site scripting and MITM related attacks. -Example +Example: .. sourcecode:: html Content-Security-Policy: default-src https:; script-src 'nonce-{random}'; object-src 'none' -To learn more check `this `_ +See also `Content Security Policy `_. * `flask-talisman `_ * `flask-csp `_ @@ -143,10 +143,9 @@ To learn more check `this `_ `HTTP Strict Transport Security `_ (HSTS) ------------------------------------------------------------------------------------------------------------------------------ +Redirects http requests to https on all urls, preventing MITM attacks. -For automatically redirect HTTP to HTTPS on all the website url's and prevent MITM attacks - -Example +Example: .. sourcecode:: html @@ -154,8 +153,7 @@ Example Strict-Transport-Security: max-age=; includeSubDomains Strict-Transport-Security: max-age=; preload -To learn more check `this `_ - +See also `Strict Transport Security `_. * `flask-talisman `_ * `flask-sslify `_ @@ -163,8 +161,8 @@ To learn more check `this `_ (Clickjacking protection) ------------------------------------------------------------------------------------------------------------------------- -Prevents the client clicking page elements outside of the website avoiding hijacking or UI redress attacks +Prevents the client from clicking page elements outside of the website, avoiding hijacking or UI redress attacks. .. sourcecode:: html @@ -172,7 +170,7 @@ Prevents the client clicking page elements outside of the website avoiding hijac X-Frame-Options: SAMEORIGIN X-Frame-Options: ALLOW-FROM https://example.com/ -To learn more check `this `_ +See also `X-Frame-Options `_. * `flask-talisman `_ * `flask-secure-headers `_ @@ -180,14 +178,13 @@ To learn more check `this `_ ------------------------------------------------------------------------------------------------------------- -Prevents XSS by blocking requests on clients and forcing then to read the content type instead of first opening it. +Prevents XSS by blocking requests on clients and forcing them to read the content type instead of first opening it. .. sourcecode:: html X-Content-Type-Options: nosniff -To learn more check `this `_ - +See also `X-Content-Type-Options `_. * `flask-talisman `_ * `flask-secure-headers `_ @@ -195,15 +192,15 @@ To learn more check `this `_ ---------------------------------------------------------------------------------------------------------- -For setting cookies on client-side storage +For setting cookies on client-side storage. -Example +Example: .. sourcecode:: html Set-Cookie: [cookie-name]=[cookie-value] -To learn more check `this `_ +See also `HTTP cookies `_ . * `flask-talisman `_ * `flask-secure-headers `_ @@ -211,20 +208,20 @@ To learn more check `this `_ (HPKP) ------------------------------------------------------------------------------------------------------- -For associating clients with web servers throught a certificate key and prevent MITM attacks +For associating clients with web servers through a certificate key and prevent MITM attacks. -Example +Example: .. sourcecode:: html Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"] -To learn more check `this `_ +See also `Public Key Pinning `_. * `flask-talisman `_ * `flask-secure-headers `_ -References: +References ----------- * https://docs.djangoproject.com/en/1.11/topics/security/ From c47f4530a1f2a15830c1d1cb983297a580a4613d Mon Sep 17 00:00:00 2001 From: Lowell Abbott Date: Mon, 22 May 2017 23:54:28 -0700 Subject: [PATCH 204/399] Erased duplicated links on title --- docs/security.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/security.rst b/docs/security.rst index f2a1ee4e..b6c234b6 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -122,7 +122,7 @@ Security Headers This section contains a list of headers supported by Flask and some packages that implements them. -`Content Security Policy `_ (CSP) +Content Security Policy (CSP) ----------------------------------------------------------------------------- Enhance security and prevents common web vulnerabilities such as cross-site scripting and MITM related attacks. @@ -140,7 +140,7 @@ See also `Content Security Policy `_ * `flask-csp `_ * `flask-secure-headers `_ -`HTTP Strict Transport Security `_ (HSTS) +HTTP Strict Transport Security (HSTS) ------------------------------------------------------------------------------------------------------------------------------ Redirects http requests to https on all urls, preventing MITM attacks. @@ -159,7 +159,7 @@ See also `Strict Transport Security `_ * `flask-secure-headers `_ -`X-FRAME-OPTIONS `_ (Clickjacking protection) +X-FRAME-OPTIONS (Clickjacking protection) ------------------------------------------------------------------------------------------------------------------------- Prevents the client from clicking page elements outside of the website, avoiding hijacking or UI redress attacks. @@ -175,7 +175,7 @@ See also `X-Frame-Options `_ * `flask-secure-headers `_ -`X-Content-Type-Options `_ +X-Content-Type-Options ------------------------------------------------------------------------------------------------------------- Prevents XSS by blocking requests on clients and forcing them to read the content type instead of first opening it. @@ -189,7 +189,7 @@ See also `X-Content-Type-Options `_ * `flask-secure-headers `_ -`Cookie options `_ +Cookie options ---------------------------------------------------------------------------------------------------------- For setting cookies on client-side storage. @@ -205,7 +205,7 @@ See also `HTTP cookies `_ * `flask-secure-headers `_ -`HTTP Public Key Pinning `_ (HPKP) +HTTP Public Key Pinning (HPKP) ------------------------------------------------------------------------------------------------------- For associating clients with web servers through a certificate key and prevent MITM attacks. From ee7cb9d6b2ff404be33bcc0487f8b0fee806436d Mon Sep 17 00:00:00 2001 From: Lowell Abbott Date: Tue, 23 May 2017 01:54:06 -0700 Subject: [PATCH 205/399] Suggest only one package, change the sourcecode block to none --- docs/security.rst | 68 ++++++++++------------------------------------- 1 file changed, 14 insertions(+), 54 deletions(-) diff --git a/docs/security.rst b/docs/security.rst index b6c234b6..120600cc 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -105,49 +105,33 @@ vulnerabilities this behavior was changed and :func:`~flask.jsonify` now supports serializing arrays. - -SSL/HTTPS ---------- - -For implementing HTTPS on your server. - -Below are some packages that implement this protocol: - -* `flask-talisman `_ -* `flask-sslify `_ -* `flask-secure-headers `_ - Security Headers ---------------- -This section contains a list of headers supported by Flask and some packages that implements them. +This section contains a list of headers supported by Flask. +To configure HTTPS and handle the headers listed below we suggest the package `flask-talisman `. Content Security Policy (CSP) ------------------------------------------------------------------------------ +----------------------------- Enhance security and prevents common web vulnerabilities such as cross-site scripting and MITM related attacks. Example: -.. sourcecode:: html +.. sourcecode:: none Content-Security-Policy: default-src https:; script-src 'nonce-{random}'; object-src 'none' - See also `Content Security Policy `_. -* `flask-talisman `_ -* `flask-csp `_ -* `flask-secure-headers `_ - HTTP Strict Transport Security (HSTS) ------------------------------------------------------------------------------------------------------------------------------- +------------------------------------- Redirects http requests to https on all urls, preventing MITM attacks. Example: -.. sourcecode:: html +.. sourcecode:: none Strict-Transport-Security: max-age=; includeSubDomains @@ -155,16 +139,12 @@ Example: See also `Strict Transport Security `_. -* `flask-talisman `_ -* `flask-sslify `_ -* `flask-secure-headers `_ - X-FRAME-OPTIONS (Clickjacking protection) -------------------------------------------------------------------------------------------------------------------------- +----------------------------------------- Prevents the client from clicking page elements outside of the website, avoiding hijacking or UI redress attacks. -.. sourcecode:: html +.. sourcecode:: none X-Frame-Options: DENY X-Frame-Options: SAMEORIGIN @@ -172,59 +152,39 @@ Prevents the client from clicking page elements outside of the website, avoiding See also `X-Frame-Options `_. -* `flask-talisman `_ -* `flask-secure-headers `_ - X-Content-Type-Options -------------------------------------------------------------------------------------------------------------- +---------------------- Prevents XSS by blocking requests on clients and forcing them to read the content type instead of first opening it. -.. sourcecode:: html +.. sourcecode:: none X-Content-Type-Options: nosniff See also `X-Content-Type-Options `_. -* `flask-talisman `_ -* `flask-secure-headers `_ - Cookie options ----------------------------------------------------------------------------------------------------------- +-------------- For setting cookies on client-side storage. Example: -.. sourcecode:: html +.. sourcecode:: none Set-Cookie: [cookie-name]=[cookie-value] See also `HTTP cookies `_ . -* `flask-talisman `_ -* `flask-secure-headers `_ - HTTP Public Key Pinning (HPKP) -------------------------------------------------------------------------------------------------------- +------------------------------ For associating clients with web servers through a certificate key and prevent MITM attacks. Example: -.. sourcecode:: html +.. sourcecode:: none Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"] See also `Public Key Pinning `_. - -* `flask-talisman `_ -* `flask-secure-headers `_ - -References ------------ - -* https://docs.djangoproject.com/en/1.11/topics/security/ -* https://blog.appcanary.com/2017/http-security-headers.html -* https://developer.mozilla.org -* https://csp.withgoogle.com/docs/index.html From cd412b20dc64f3bfa980c9993e5a4dce39a22b94 Mon Sep 17 00:00:00 2001 From: MikeTheReader Date: Tue, 23 May 2017 07:51:57 -0700 Subject: [PATCH 206/399] Parameterize test_get_debug_flag --- tests/test_helpers.py | 34 ++++++++++++++-------------------- 1 file changed, 14 insertions(+), 20 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index a5863da9..1f623559 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -945,26 +945,20 @@ class TestSafeJoin(object): class TestHelpers(object): - def test_get_debug_flag(self, monkeypatch): - monkeypatch.setenv('FLASK_DEBUG', '') - assert get_debug_flag() == None - assert get_debug_flag(default=True) == True - - monkeypatch.setenv('FLASK_DEBUG', '0') - assert get_debug_flag() == False - assert get_debug_flag(default=True) == False - - monkeypatch.setenv('FLASK_DEBUG', 'False') - assert get_debug_flag() == False - assert get_debug_flag(default=True) == False - - monkeypatch.setenv('FLASK_DEBUG', 'No') - assert get_debug_flag() == False - assert get_debug_flag(default=True) == False - - monkeypatch.setenv('FLASK_DEBUG', 'True') - assert get_debug_flag() == True - assert get_debug_flag(default=True) == True + @pytest.mark.parametrize("debug, expected_flag, expected_default_flag", [ + ('', None, True), + ('0', False, False), + ('False', False, False), + ('No', False, False), + ('True', True, True) + ]) + def test_get_debug_flag(self, monkeypatch, debug, expected_flag, expected_default_flag): + monkeypatch.setenv('FLASK_DEBUG', debug) + if expected_flag is None: + assert get_debug_flag() is None + else: + assert get_debug_flag() == expected_flag + assert get_debug_flag(default=True) == expected_default_flag def test_make_response(self): app = flask.Flask(__name__) From 7c882a457b734cd0a38d41c7e930e375a6ce6e4c Mon Sep 17 00:00:00 2001 From: MikeTheReader Date: Tue, 23 May 2017 07:59:53 -0700 Subject: [PATCH 207/399] Replace double quotes with single quotes --- tests/test_helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 1f623559..5af95f18 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -945,7 +945,7 @@ class TestSafeJoin(object): class TestHelpers(object): - @pytest.mark.parametrize("debug, expected_flag, expected_default_flag", [ + @pytest.mark.parametrize('debug, expected_flag, expected_default_flag', [ ('', None, True), ('0', False, False), ('False', False, False), From 2339f1f24e29adfd263cb6ebcb98c09ecb4396d9 Mon Sep 17 00:00:00 2001 From: Christopher Sorenson Date: Mon, 22 May 2017 18:39:07 -0500 Subject: [PATCH 208/399] making some updates to the CONTRIBUTING.rst --- CONTRIBUTING.rst | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 852e44be..80b6af35 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -24,12 +24,31 @@ Reporting issues Submitting patches ================== + +- `Make sure you have a github account `_ +- `Download and install latest version of git on your + machine `_ +- `Set up your username in git + `_ +- `Set up your email in git + `_ +- Fork flask to your github account (Click the Fork button) +- `Copy your github fork locally + `_ +- Create a branch to identify the issue you would like to work + on (eg 2287-dry-test-suite) +- Using your favorite editor, make your changes, + `committing as you go `_ - Include tests if your patch is supposed to solve a bug, and explain clearly under which circumstances the bug happens. Make sure the test fails without your patch. - - Try to follow `PEP8 `_, but you may ignore the line-length-limit if following it would make the code uglier. +- `Run tests. + `_ + When tests pass push changes to github and `create pull request + `_ +- Celebrate 🎉 Running the testsuite From 5963cb5a5172bce3a1ff7b0f8c64230f043c9cfd Mon Sep 17 00:00:00 2001 From: bovarysme Date: Tue, 23 May 2017 18:21:29 +0200 Subject: [PATCH 209/399] Use the yield syntax in pytest's fixtures --- examples/minitwit/tests/test_minitwit.py | 12 +++++------- tests/conftest.py | 19 ++++++++++--------- tests/test_ext.py | 22 ++++++++++------------ 3 files changed, 25 insertions(+), 28 deletions(-) diff --git a/examples/minitwit/tests/test_minitwit.py b/examples/minitwit/tests/test_minitwit.py index 50ca26d9..c8992e57 100644 --- a/examples/minitwit/tests/test_minitwit.py +++ b/examples/minitwit/tests/test_minitwit.py @@ -15,18 +15,16 @@ from minitwit import minitwit @pytest.fixture -def client(request): +def client(): db_fd, minitwit.app.config['DATABASE'] = tempfile.mkstemp() client = minitwit.app.test_client() with minitwit.app.app_context(): minitwit.init_db() - def teardown(): - """Get rid of the database again after each test.""" - os.close(db_fd) - os.unlink(minitwit.app.config['DATABASE']) - request.addfinalizer(teardown) - return client + yield client + + os.close(db_fd) + os.unlink(minitwit.app.config['DATABASE']) def register(client, username, password, password2=None, email=None): diff --git a/tests/conftest.py b/tests/conftest.py index 8c9541de..eb130db6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,16 +22,17 @@ def test_apps(monkeypatch): os.path.dirname(__file__), 'test_apps')) ) + @pytest.fixture(autouse=True) -def leak_detector(request): - def ensure_clean_request_context(): - # make sure we're not leaking a request context since we are - # testing flask internally in debug mode in a few cases - leaks = [] - while flask._request_ctx_stack.top is not None: - leaks.append(flask._request_ctx_stack.pop()) - assert leaks == [] - request.addfinalizer(ensure_clean_request_context) +def leak_detector(): + yield + + # make sure we're not leaking a request context since we are + # testing flask internally in debug mode in a few cases + leaks = [] + while flask._request_ctx_stack.top is not None: + leaks.append(flask._request_ctx_stack.pop()) + assert leaks == [] @pytest.fixture(params=(True, False)) diff --git a/tests/test_ext.py b/tests/test_ext.py index ebb5f02d..48214905 100644 --- a/tests/test_ext.py +++ b/tests/test_ext.py @@ -21,19 +21,18 @@ from flask._compat import PY2 @pytest.fixture(autouse=True) -def disable_extwarnings(request, recwarn): +def disable_extwarnings(recwarn): from flask.exthook import ExtDeprecationWarning - def inner(): - assert set(w.category for w in recwarn.list) \ - <= set([ExtDeprecationWarning]) - recwarn.clear() + yield - request.addfinalizer(inner) + assert set(w.category for w in recwarn.list) \ + <= set([ExtDeprecationWarning]) + recwarn.clear() @pytest.fixture(autouse=True) -def importhook_setup(monkeypatch, request): +def importhook_setup(monkeypatch): # we clear this out for various reasons. The most important one is # that a real flaskext could be in there which would disable our # fake package. Secondly we want to make sure that the flaskext @@ -58,12 +57,11 @@ def importhook_setup(monkeypatch, request): import_hooks += 1 assert import_hooks == 1 - def teardown(): - from flask import ext - for key in ext.__dict__: - assert '.' not in key + yield - request.addfinalizer(teardown) + from flask import ext + for key in ext.__dict__: + assert '.' not in key @pytest.fixture From 668061a5fc928a5055815acf818b02baf1aef37b Mon Sep 17 00:00:00 2001 From: Florian Sachs Date: Sun, 8 Jan 2017 20:42:41 +0100 Subject: [PATCH 210/399] Register errorhandlers for Exceptions Allow a default errorhandler by registering an errorhandler for HTTPException tests included --- flask/app.py | 11 ++++++++++- tests/test_user_error_handler.py | 25 ++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/flask/app.py b/flask/app.py index 13a56c68..1251d2fb 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1484,7 +1484,16 @@ class Flask(_PackageBoundObject): return handler # fall back to app handlers - return find_handler(self.error_handler_spec[None].get(code)) + handler = find_handler(self.error_handler_spec[None].get(code)) + if handler is not None: + return handler + + try: + handler = find_handler(self.error_handler_spec[None][None]) + except KeyError: + handler = None + + return handler def handle_http_exception(self, e): """Handles an HTTP exception. By default this will invoke the diff --git a/tests/test_user_error_handler.py b/tests/test_user_error_handler.py index 11677433..b6d4ca34 100644 --- a/tests/test_user_error_handler.py +++ b/tests/test_user_error_handler.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -from werkzeug.exceptions import Forbidden, InternalServerError +from werkzeug.exceptions import Forbidden, InternalServerError, HTTPException, NotFound import flask @@ -32,6 +32,29 @@ def test_error_handler_no_match(): assert c.get('/keyerror').data == b'KeyError' +def test_default_error_handler(): + app = flask.Flask(__name__) + + @app.errorhandler(HTTPException) + def catchall_errorhandler(e): + assert isinstance(e, HTTPException) + assert isinstance(e, NotFound) + return 'default' + + @app.errorhandler(Forbidden) + def catchall_errorhandler(e): + assert isinstance(e, Forbidden) + return 'forbidden' + + @app.route('/forbidden') + def forbidden(): + raise Forbidden() + + c = app.test_client() + assert c.get('/undefined').data == b'default' + assert c.get('/forbidden').data == b'forbidden' + + def test_error_handler_subclass(): app = flask.Flask(__name__) From 637eae548935ad1434459c1afac286c8686abeed Mon Sep 17 00:00:00 2001 From: cerickson Date: Mon, 22 May 2017 15:57:31 -0700 Subject: [PATCH 211/399] Added support for generic HTTPException handlers on app and blueprints Error handlers are now returned in order of blueprint:code, app:code, blueprint:HTTPException, app:HTTPException, None Corresponding tests also added. Ref issue #941, pr #1383, #2082, #2144 --- flask/app.py | 33 ++++++------- tests/test_user_error_handler.py | 80 ++++++++++++++++++++++---------- 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/flask/app.py b/flask/app.py index 1251d2fb..f4c73978 100644 --- a/flask/app.py +++ b/flask/app.py @@ -133,6 +133,8 @@ class Flask(_PackageBoundObject): :param static_folder: the folder with static files that should be served at `static_url_path`. Defaults to the ``'static'`` folder in the root path of the application. + folder in the root path of the application. Defaults + to None. :param host_matching: sets the app's ``url_map.host_matching`` to the given given value. Defaults to False. :param static_host: the host to use when adding the static route. Defaults @@ -1460,15 +1462,17 @@ class Flask(_PackageBoundObject): return f def _find_error_handler(self, e): - """Finds a registered error handler for the request’s blueprint. - Otherwise falls back to the app, returns None if not a suitable - handler is found. + """Find a registered error handler for a request in this order: + blueprint handler for a specific code, app handler for a specific code, + blueprint generic HTTPException handler, app generic HTTPException handler, + and returns None if a suitable handler is not found. """ exc_class, code = self._get_exc_class_and_code(type(e)) def find_handler(handler_map): if not handler_map: return + for cls in exc_class.__mro__: handler = handler_map.get(cls) if handler is not None: @@ -1476,24 +1480,13 @@ class Flask(_PackageBoundObject): handler_map[exc_class] = handler return handler - # try blueprint handlers - handler = find_handler(self.error_handler_spec - .get(request.blueprint, {}) - .get(code)) - if handler is not None: - return handler - - # fall back to app handlers - handler = find_handler(self.error_handler_spec[None].get(code)) - if handler is not None: - return handler - - try: - handler = find_handler(self.error_handler_spec[None][None]) - except KeyError: - handler = None + # check for any in blueprint or app + for name, c in ((request.blueprint, code), (None, code), + (request.blueprint, None), (None, None)): + handler = find_handler(self.error_handler_spec.get(name, {}).get(c)) - return handler + if handler: + return handler def handle_http_exception(self, e): """Handles an HTTP exception. By default this will invoke the diff --git a/tests/test_user_error_handler.py b/tests/test_user_error_handler.py index b6d4ca34..24ffe73f 100644 --- a/tests/test_user_error_handler.py +++ b/tests/test_user_error_handler.py @@ -1,5 +1,10 @@ # -*- coding: utf-8 -*- -from werkzeug.exceptions import Forbidden, InternalServerError, HTTPException, NotFound +from werkzeug.exceptions import ( + Forbidden, + InternalServerError, + HTTPException, + NotFound + ) import flask @@ -32,29 +37,6 @@ def test_error_handler_no_match(): assert c.get('/keyerror').data == b'KeyError' -def test_default_error_handler(): - app = flask.Flask(__name__) - - @app.errorhandler(HTTPException) - def catchall_errorhandler(e): - assert isinstance(e, HTTPException) - assert isinstance(e, NotFound) - return 'default' - - @app.errorhandler(Forbidden) - def catchall_errorhandler(e): - assert isinstance(e, Forbidden) - return 'forbidden' - - @app.route('/forbidden') - def forbidden(): - raise Forbidden() - - c = app.test_client() - assert c.get('/undefined').data == b'default' - assert c.get('/forbidden').data == b'forbidden' - - def test_error_handler_subclass(): app = flask.Flask(__name__) @@ -161,3 +143,53 @@ def test_error_handler_blueprint(): assert c.get('/error').data == b'app-error' assert c.get('/bp/error').data == b'bp-error' + + +def test_default_error_handler(): + bp = flask.Blueprint('bp', __name__) + + @bp.errorhandler(HTTPException) + def bp_exception_handler(e): + assert isinstance(e, HTTPException) + assert isinstance(e, NotFound) + return 'bp-default' + + @bp.errorhandler(Forbidden) + def bp_exception_handler(e): + assert isinstance(e, Forbidden) + return 'bp-forbidden' + + @bp.route('/undefined') + def bp_registered_test(): + raise NotFound() + + @bp.route('/forbidden') + def bp_forbidden_test(): + raise Forbidden() + + app = flask.Flask(__name__) + + @app.errorhandler(HTTPException) + def catchall_errorhandler(e): + assert isinstance(e, HTTPException) + assert isinstance(e, NotFound) + return 'default' + + @app.errorhandler(Forbidden) + def catchall_errorhandler(e): + assert isinstance(e, Forbidden) + return 'forbidden' + + @app.route('/forbidden') + def forbidden(): + raise Forbidden() + + app.register_blueprint(bp, url_prefix='/bp') + + c = app.test_client() + assert c.get('/bp/undefined').data == b'bp-default' + assert c.get('/bp/forbidden').data == b'bp-forbidden' + assert c.get('/undefined').data == b'default' + assert c.get('/forbidden').data == b'forbidden' + + From 4f815015b803a146ddc24a2dfadd9ed9b22d7de7 Mon Sep 17 00:00:00 2001 From: cerickson Date: Mon, 22 May 2017 15:57:31 -0700 Subject: [PATCH 212/399] Added support for generic HTTPException handlers on app and blueprints Error handlers are now returned in order of blueprint:code, app:code, blueprint:HTTPException, app:HTTPException, None Corresponding tests also added. Ref issue #941, pr #1383, #2082, #2144 --- flask/app.py | 33 ++++++------- tests/test_user_error_handler.py | 80 ++++++++++++++++++++++---------- 2 files changed, 69 insertions(+), 44 deletions(-) diff --git a/flask/app.py b/flask/app.py index 1251d2fb..f4c73978 100644 --- a/flask/app.py +++ b/flask/app.py @@ -133,6 +133,8 @@ class Flask(_PackageBoundObject): :param static_folder: the folder with static files that should be served at `static_url_path`. Defaults to the ``'static'`` folder in the root path of the application. + folder in the root path of the application. Defaults + to None. :param host_matching: sets the app's ``url_map.host_matching`` to the given given value. Defaults to False. :param static_host: the host to use when adding the static route. Defaults @@ -1460,15 +1462,17 @@ class Flask(_PackageBoundObject): return f def _find_error_handler(self, e): - """Finds a registered error handler for the request’s blueprint. - Otherwise falls back to the app, returns None if not a suitable - handler is found. + """Find a registered error handler for a request in this order: + blueprint handler for a specific code, app handler for a specific code, + blueprint generic HTTPException handler, app generic HTTPException handler, + and returns None if a suitable handler is not found. """ exc_class, code = self._get_exc_class_and_code(type(e)) def find_handler(handler_map): if not handler_map: return + for cls in exc_class.__mro__: handler = handler_map.get(cls) if handler is not None: @@ -1476,24 +1480,13 @@ class Flask(_PackageBoundObject): handler_map[exc_class] = handler return handler - # try blueprint handlers - handler = find_handler(self.error_handler_spec - .get(request.blueprint, {}) - .get(code)) - if handler is not None: - return handler - - # fall back to app handlers - handler = find_handler(self.error_handler_spec[None].get(code)) - if handler is not None: - return handler - - try: - handler = find_handler(self.error_handler_spec[None][None]) - except KeyError: - handler = None + # check for any in blueprint or app + for name, c in ((request.blueprint, code), (None, code), + (request.blueprint, None), (None, None)): + handler = find_handler(self.error_handler_spec.get(name, {}).get(c)) - return handler + if handler: + return handler def handle_http_exception(self, e): """Handles an HTTP exception. By default this will invoke the diff --git a/tests/test_user_error_handler.py b/tests/test_user_error_handler.py index b6d4ca34..24ffe73f 100644 --- a/tests/test_user_error_handler.py +++ b/tests/test_user_error_handler.py @@ -1,5 +1,10 @@ # -*- coding: utf-8 -*- -from werkzeug.exceptions import Forbidden, InternalServerError, HTTPException, NotFound +from werkzeug.exceptions import ( + Forbidden, + InternalServerError, + HTTPException, + NotFound + ) import flask @@ -32,29 +37,6 @@ def test_error_handler_no_match(): assert c.get('/keyerror').data == b'KeyError' -def test_default_error_handler(): - app = flask.Flask(__name__) - - @app.errorhandler(HTTPException) - def catchall_errorhandler(e): - assert isinstance(e, HTTPException) - assert isinstance(e, NotFound) - return 'default' - - @app.errorhandler(Forbidden) - def catchall_errorhandler(e): - assert isinstance(e, Forbidden) - return 'forbidden' - - @app.route('/forbidden') - def forbidden(): - raise Forbidden() - - c = app.test_client() - assert c.get('/undefined').data == b'default' - assert c.get('/forbidden').data == b'forbidden' - - def test_error_handler_subclass(): app = flask.Flask(__name__) @@ -161,3 +143,53 @@ def test_error_handler_blueprint(): assert c.get('/error').data == b'app-error' assert c.get('/bp/error').data == b'bp-error' + + +def test_default_error_handler(): + bp = flask.Blueprint('bp', __name__) + + @bp.errorhandler(HTTPException) + def bp_exception_handler(e): + assert isinstance(e, HTTPException) + assert isinstance(e, NotFound) + return 'bp-default' + + @bp.errorhandler(Forbidden) + def bp_exception_handler(e): + assert isinstance(e, Forbidden) + return 'bp-forbidden' + + @bp.route('/undefined') + def bp_registered_test(): + raise NotFound() + + @bp.route('/forbidden') + def bp_forbidden_test(): + raise Forbidden() + + app = flask.Flask(__name__) + + @app.errorhandler(HTTPException) + def catchall_errorhandler(e): + assert isinstance(e, HTTPException) + assert isinstance(e, NotFound) + return 'default' + + @app.errorhandler(Forbidden) + def catchall_errorhandler(e): + assert isinstance(e, Forbidden) + return 'forbidden' + + @app.route('/forbidden') + def forbidden(): + raise Forbidden() + + app.register_blueprint(bp, url_prefix='/bp') + + c = app.test_client() + assert c.get('/bp/undefined').data == b'bp-default' + assert c.get('/bp/forbidden').data == b'bp-forbidden' + assert c.get('/undefined').data == b'default' + assert c.get('/forbidden').data == b'forbidden' + + From 361dba7e3af06be8f81c7bdd5067c59e08df2df9 Mon Sep 17 00:00:00 2001 From: cerickson Date: Tue, 23 May 2017 10:49:01 -0700 Subject: [PATCH 213/399] removed dupe text from merge --- flask/app.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/flask/app.py b/flask/app.py index f4c73978..6e45774e 100644 --- a/flask/app.py +++ b/flask/app.py @@ -133,8 +133,6 @@ class Flask(_PackageBoundObject): :param static_folder: the folder with static files that should be served at `static_url_path`. Defaults to the ``'static'`` folder in the root path of the application. - folder in the root path of the application. Defaults - to None. :param host_matching: sets the app's ``url_map.host_matching`` to the given given value. Defaults to False. :param static_host: the host to use when adding the static route. Defaults From 532ca2e08961b6ffd925c28581c45795986755ee Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 23 May 2017 11:18:48 -0700 Subject: [PATCH 214/399] reorganize git instructions --- CONTRIBUTING.rst | 58 ++++++++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 24 deletions(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 80b6af35..22828608 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -24,32 +24,42 @@ Reporting issues Submitting patches ================== - -- `Make sure you have a github account `_ -- `Download and install latest version of git on your - machine `_ -- `Set up your username in git - `_ -- `Set up your email in git - `_ -- Fork flask to your github account (Click the Fork button) -- `Copy your github fork locally - `_ -- Create a branch to identify the issue you would like to work - on (eg 2287-dry-test-suite) -- Using your favorite editor, make your changes, - `committing as you go `_ -- Include tests if your patch is supposed to solve a bug, and explain - clearly under which circumstances the bug happens. Make sure the test fails - without your patch. -- Try to follow `PEP8 `_, but you - may ignore the line-length-limit if following it would make the code uglier. -- `Run tests. - `_ - When tests pass push changes to github and `create pull request - `_ +First time setup +---------------- + +- Download and install the `latest version of git`_. +- Configure git with your `username`_ and `email`_. +- Make sure you have a `GitHub account`_. +- Fork Flask to your GitHub account by clicking the `Fork`_ button. +- `Clone`_ your GitHub fork locally. +- Add the main repository as a remote to update later. + ``git remote add pallets https://github.com/pallets/flask`` + +.. _GitHub account: https://github.com/join +.. _latest version of git: https://git-scm.com/downloads +.. _username: https://help.github.com/articles/setting-your-username-in-git/ +.. _email: https://help.github.com/articles/setting-your-email-in-git/ +.. _Fork: https://github.com/pallets/flask/pull/2305#fork-destination-box +.. _Clone: https://help.github.com/articles/fork-a-repo/#step-2-create-a-local-clone-of-your-fork + +Start coding +------------ + +- Create a branch to identify the issue you would like to work on (e.g. + ``2287-dry-test-suite``) +- Using your favorite editor, make your changes, `committing as you go`_. +- Try to follow `PEP8`_, but you may ignore the line length limit if following + it would make the code uglier. +- Include tests that cover any code changes you make. Make sure the test fails + without your patch. `Run the tests. `_. +- Push your commits to GitHub and `create a pull request`_. - Celebrate 🎉 +.. _committing as you go: http://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes +.. _PEP8: https://pep8.org/ +.. _create a pull request: https://help.github.com/articles/creating-a-pull-request/ + +.. _contributing-testsuite: Running the testsuite --------------------- From 954d9ca0b8519ee651daba64ca9292aaa74fff0d Mon Sep 17 00:00:00 2001 From: Levi Roth Date: Tue, 23 May 2017 14:30:39 -0400 Subject: [PATCH 215/399] Added documentation for PowerShell environment variables --- docs/quickstart.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index aea776f3..d56fa8e2 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -50,7 +50,14 @@ to tell your terminal the application to work with by exporting the $ flask run * Running on http://127.0.0.1:5000/ -If you are on Windows you need to use ``set`` instead of ``export``. +If you are on Windows, the environment variable syntax depends on command line +interpreter. On Command Prompt:: + + C:\path\to\app>set FLASK_APP=hello.py + +And on PowerShell:: + + PS C:\path\to\app> $env:FLASK_APP = "hello.py" Alternatively you can use :command:`python -m flask`:: From 8858135043a42df9f0728eb548d3272b476100b6 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 23 May 2017 11:50:14 -0700 Subject: [PATCH 216/399] Update testing.rst --- docs/testing.rst | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index 67b8aaea..c00d06e0 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -47,17 +47,23 @@ the application for testing and initializes a new database.:: import os import tempfile + import pytest + from flaskr import flaskr + @pytest.fixture def client(request): db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() flaskr.app.config['TESTING'] = True client = flaskr.app.test_client() + with flaskr.app.app_context(): flaskr.init_db() + yield client + os.close(db_fd) os.unlink(flaskr.app.config['DATABASE']) @@ -77,8 +83,8 @@ low-level file handle and a random file name, the latter we use as database name. We just have to keep the `db_fd` around so that we can use the :func:`os.close` function to close the file. -To delete the database after the test, we close the file and remove it -from the filesystem. +To delete the database after the test, the fixture closes the file and removes +it from the filesystem. If we now run the test suite, we should see the following output:: @@ -107,6 +113,7 @@ test function to :file:`test_flaskr.py`, like this:: def test_empty_db(client): """Start with a blank database.""" + rv = client.get('/') assert b'No entries here so far' in rv.data @@ -148,6 +155,7 @@ Add the following two functions to your :file:`test_flaskr.py` file:: password=password ), follow_redirects=True) + def logout(client): return client.get('/logout', follow_redirects=True) @@ -155,17 +163,18 @@ Now we can easily test that logging in and out works and that it fails with invalid credentials. Add this new test function:: def test_login_logout(client): - """Make sure login and logout works""" - rv = login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD']) + """Make sure login and logout works.""" + + rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD']) assert b'You were logged in' in rv.data + rv = logout(client) assert b'You were logged out' in rv.data - rv = login(client, flaskr.app.config['USERNAME'] + 'x', - flaskr.app.config['PASSWORD']) + + rv = login(client, flaskr.app.config['USERNAME'] + 'x', flaskr.app.config['PASSWORD']) assert b'Invalid username' in rv.data - rv = login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD'] + 'x') + + rv = login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD'] + 'x') assert b'Invalid password' in rv.data Test Adding Messages @@ -175,9 +184,9 @@ We should also test that adding messages works. Add a new test function like this:: def test_messages(client): - """Test that messages work""" - login(client, flaskr.app.config['USERNAME'], - flaskr.app.config['PASSWORD']) + """Test that messages work.""" + + login(client, flaskr.app.config['USERNAME'], flaskr.app.config['PASSWORD']) rv = client.post('/add', data=dict( title='', text='HTML allowed here' @@ -207,11 +216,9 @@ For more complex tests with headers and status codes, check out the `MiniTwit Example`_ from the sources which contains a larger test suite. - .. _MiniTwit Example: https://github.com/pallets/flask/tree/master/examples/minitwit/ - Other Testing Tricks -------------------- From 75f537fb87ea3d60465a82c5c221104bcceef1f2 Mon Sep 17 00:00:00 2001 From: kaveh Date: Tue, 23 May 2017 11:51:13 -0700 Subject: [PATCH 217/399] Adds provide_automatic_options to Class-based Views --- flask/views.py | 4 ++++ tests/test_views.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/flask/views.py b/flask/views.py index 848ccb0b..b3027970 100644 --- a/flask/views.py +++ b/flask/views.py @@ -51,6 +51,9 @@ class View(object): #: A list of methods this view can handle. methods = None + #: Setting this disables or force-enables the automatic options handling. + provide_automatic_options = None + #: The canonical way to decorate class-based views is to decorate the #: return value of as_view(). However since this moves parts of the #: logic from the class declaration to the place where it's hooked @@ -99,6 +102,7 @@ class View(object): view.__doc__ = cls.__doc__ view.__module__ = cls.__module__ view.methods = cls.methods + view.provide_automatic_options = cls.provide_automatic_options return view diff --git a/tests/test_views.py b/tests/test_views.py index 65981dbd..896880c0 100644 --- a/tests/test_views.py +++ b/tests/test_views.py @@ -109,6 +109,43 @@ def test_view_decorators(): assert rv.headers['X-Parachute'] == 'awesome' assert rv.data == b'Awesome' +def test_view_provide_automatic_options_attr(): + app = flask.Flask(__name__) + + class Index1(flask.views.View): + provide_automatic_options = False + def dispatch_request(self): + return 'Hello World!' + + app.add_url_rule('/', view_func=Index1.as_view('index')) + c = app.test_client() + rv = c.open('/', method='OPTIONS') + assert rv.status_code == 405 + + app = flask.Flask(__name__) + + class Index2(flask.views.View): + methods = ['OPTIONS'] + provide_automatic_options = True + def dispatch_request(self): + return 'Hello World!' + + app.add_url_rule('/', view_func=Index2.as_view('index')) + c = app.test_client() + rv = c.open('/', method='OPTIONS') + assert sorted(rv.allow) == ['OPTIONS'] + + app = flask.Flask(__name__) + + class Index3(flask.views.View): + def dispatch_request(self): + return 'Hello World!' + + app.add_url_rule('/', view_func=Index3.as_view('index')) + c = app.test_client() + rv = c.open('/', method='OPTIONS') + assert 'OPTIONS' in rv.allow + def test_implicit_head(): app = flask.Flask(__name__) From 09a0d2ebd1c832977b46be6d0cbd7b42944ffc49 Mon Sep 17 00:00:00 2001 From: Lowell Abbott Date: Tue, 23 May 2017 12:26:43 -0700 Subject: [PATCH 218/399] Re-order by semantic. Fix link on flask-talismand and re-word many concepts --- docs/security.rst | 59 ++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 26 deletions(-) diff --git a/docs/security.rst b/docs/security.rst index 120600cc..59767139 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -109,37 +109,37 @@ Security Headers ---------------- This section contains a list of headers supported by Flask. -To configure HTTPS and handle the headers listed below we suggest the package `flask-talisman `. +To configure HTTPS and handle the headers listed below we suggest the package `flask-talisman `_. -Content Security Policy (CSP) ------------------------------ +HTTP Strict Transport Security (HSTS) +------------------------------------- -Enhance security and prevents common web vulnerabilities such as cross-site scripting and MITM related attacks. +Redirects http requests to https on all urls, preventing Man-in-the-middle (MITM) attacks. Example: .. sourcecode:: none - Content-Security-Policy: default-src https:; script-src 'nonce-{random}'; object-src 'none' + Strict-Transport-Security: max-age=; includeSubDomains + Strict-Transport-Security: max-age=; preload -See also `Content Security Policy `_. +See also `Strict Transport Security `_. -HTTP Strict Transport Security (HSTS) -------------------------------------- +HTTP Public Key Pinning (HPKP) +------------------------------ -Redirects http requests to https on all urls, preventing MITM attacks. +This enables your web server to authenticate with a client browser using a specific certificate key to prevent Man-in-the-middle (MITM) attacks. Example: .. sourcecode:: none - - Strict-Transport-Security: max-age=; includeSubDomains - Strict-Transport-Security: max-age=; preload -See also `Strict Transport Security `_. + Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"] + +See also `Public Key Pinning `_. -X-FRAME-OPTIONS (Clickjacking protection) +X-Frame-Options (Clickjacking protection) ----------------------------------------- Prevents the client from clicking page elements outside of the website, avoiding hijacking or UI redress attacks. @@ -155,7 +155,7 @@ See also `X-Frame-Options `_. -Cookie options --------------- +Content Security Policy (CSP) +----------------------------- -For setting cookies on client-side storage. +Enhances security and prevents common web vulnerabilities such as cross-site scripting (XSS) and Man-in-the-middle (MITM) related attacks. Example: .. sourcecode:: none - Set-Cookie: [cookie-name]=[cookie-value] + Content-Security-Policy: default-src https:; script-src 'nonce-{random}'; object-src 'none' -See also `HTTP cookies `_ . +See also `Content Security Policy `_. -HTTP Public Key Pinning (HPKP) ------------------------------- +Cookie options +-------------- + +While these headers are not directly security related, they have important options that may affect your flask application. -For associating clients with web servers through a certificate key and prevent MITM attacks. +- ``Secure`` limits your cookies to HTTPS traffic only. +- ``HttpOnly`` protects the contents of your cookie from being visible to XSS. +- ``SameSite`` ensures that cookies can only be requested from the same domain that created them but this feature is not yet fully supported across all browsers. Example: .. sourcecode:: none + + Set-Cookie: [cookie-name]=[cookie-value] - Public-Key-Pins: pin-sha256="base64=="; max-age=expireTime [; includeSubDomains][; report-uri="reportURI"] +See also: -See also `Public Key Pinning `_. +- Mozilla guide to `HTTP cookies `_. +- `OWASP HTTP Only `_. From fe27d04cc196d65af17ac6cf67574fa61d5b4828 Mon Sep 17 00:00:00 2001 From: bovarysme Date: Tue, 23 May 2017 22:22:16 +0200 Subject: [PATCH 219/399] Fix a small oversight in the testing docs --- docs/testing.rst | 2 +- examples/flaskr/tests/test_flaskr.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index c00d06e0..fbd3fad5 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -54,7 +54,7 @@ the application for testing and initializes a new database.:: @pytest.fixture - def client(request): + def client(): db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() flaskr.app.config['TESTING'] = True client = flaskr.app.test_client() diff --git a/examples/flaskr/tests/test_flaskr.py b/examples/flaskr/tests/test_flaskr.py index df32cd4b..493067ee 100644 --- a/examples/flaskr/tests/test_flaskr.py +++ b/examples/flaskr/tests/test_flaskr.py @@ -16,7 +16,7 @@ from flaskr import flaskr @pytest.fixture -def client(request): +def client(): db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() flaskr.app.config['TESTING'] = True client = flaskr.app.test_client() From ae41df9a77ceab4469029b043e38333b8654e1da Mon Sep 17 00:00:00 2001 From: Hendrik Makait Date: Tue, 23 May 2017 13:46:45 -0700 Subject: [PATCH 220/399] Check if app factory takes script_info argument and call it with(out) script_info as an argument depending on that --- flask/_compat.py | 2 ++ flask/cli.py | 30 +++++++++++++++++------ tests/test_cli.py | 62 +++++++++++++++++++++++++++++++---------------- 3 files changed, 66 insertions(+), 28 deletions(-) diff --git a/flask/_compat.py b/flask/_compat.py index 071628fc..173b3689 100644 --- a/flask/_compat.py +++ b/flask/_compat.py @@ -25,6 +25,7 @@ if not PY2: itervalues = lambda d: iter(d.values()) iteritems = lambda d: iter(d.items()) + from inspect import getfullargspec as getargspec from io import StringIO def reraise(tp, value, tb=None): @@ -43,6 +44,7 @@ else: itervalues = lambda d: d.itervalues() iteritems = lambda d: d.iteritems() + from inspect import getargspec from cStringIO import StringIO exec('def reraise(tp, value, tb=None):\n raise tp, value, tb') diff --git a/flask/cli.py b/flask/cli.py index 6aa66f4f..ea294ae6 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -22,13 +22,14 @@ from . import __version__ from ._compat import iteritems, reraise from .globals import current_app from .helpers import get_debug_flag +from ._compat import getargspec class NoAppException(click.UsageError): """Raised if an application cannot be found or loaded.""" -def find_best_app(module): +def find_best_app(script_info, module): """Given a module instance this tries to find the best possible application in the module or raises an exception. """ @@ -60,8 +61,8 @@ def find_best_app(module): if callable(app_factory): try: - app = app_factory() - + app = check_factory_for_script_info_and_call(app_factory, + script_info) if isinstance(app, Flask): return app except TypeError: @@ -79,6 +80,21 @@ def find_best_app(module): ) +def check_factory_for_script_info_and_call(func, script_info): + """Given a function this checks if the function has an argument named + script_info or just a single argument and calls the function with + script_info if so. Otherwise calls the function without any arguments and + returns the result.""" + arguments = getargspec(func).args + if 'script_info' in arguments: + result = func(script_info=script_info) + elif len(arguments) == 1: + result = func(script_info) + else: + result = func() + return result + + 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. @@ -108,7 +124,7 @@ def prepare_exec_for_file(filename): return '.'.join(module[::-1]) -def locate_app(app_id): +def locate_app(script_info, app_id): """Attempts to locate the application.""" __traceback_hide__ = True if ':' in app_id: @@ -134,7 +150,7 @@ def locate_app(app_id): mod = sys.modules[module] if app_obj is None: - app = find_best_app(mod) + app = find_best_app(script_info, mod) else: app = getattr(mod, app_obj, None) if app is None: @@ -259,7 +275,7 @@ class ScriptInfo(object): if self._loaded_app is not None: return self._loaded_app if self.create_app is not None: - rv = self.create_app(self) + rv = check_factory_for_script_info_and_call(self.create_app, self) else: if not self.app_import_path: raise NoAppException( @@ -267,7 +283,7 @@ class ScriptInfo(object): 'the FLASK_APP environment variable.\n\nFor more ' 'information see ' 'http://flask.pocoo.org/docs/latest/quickstart/') - rv = locate_app(self.app_import_path) + rv = locate_app(self, self.app_import_path) debug = get_debug_flag() if debug is not None: rv.debug = debug diff --git a/tests/test_cli.py b/tests/test_cli.py index bbb6fe58..1c843d61 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -39,60 +39,75 @@ def test_cli_name(test_apps): def test_find_best_app(test_apps): """Test if `find_best_app` behaves as expected with different combinations of input.""" + script_info = ScriptInfo() class Module: app = Flask('appname') - assert find_best_app(Module) == Module.app + assert find_best_app(script_info, Module) == Module.app class Module: application = Flask('appname') - assert find_best_app(Module) == Module.application + assert find_best_app(script_info, Module) == Module.application class Module: myapp = Flask('appname') - assert find_best_app(Module) == Module.myapp + assert find_best_app(script_info, Module) == Module.myapp class Module: @staticmethod def create_app(): return Flask('appname') - assert isinstance(find_best_app(Module), Flask) - assert find_best_app(Module).name == 'appname' + assert isinstance(find_best_app(script_info, Module), Flask) + assert find_best_app(script_info, Module).name == 'appname' + + class Module: + @staticmethod + def create_app(foo): + return Flask('appname') + assert isinstance(find_best_app(script_info, Module), Flask) + assert find_best_app(script_info, Module).name == 'appname' + + class Module: + @staticmethod + def create_app(foo=None, script_info=None): + return Flask('appname') + assert isinstance(find_best_app(script_info, Module), Flask) + assert find_best_app(script_info, Module).name == 'appname' class Module: @staticmethod def make_app(): return Flask('appname') - assert isinstance(find_best_app(Module), Flask) - assert find_best_app(Module).name == 'appname' + assert isinstance(find_best_app(script_info, Module), Flask) + assert find_best_app(script_info, Module).name == 'appname' class Module: myapp = Flask('appname1') @staticmethod def create_app(): return Flask('appname2') - assert find_best_app(Module) == Module.myapp + assert find_best_app(script_info, Module) == Module.myapp class Module: myapp = Flask('appname1') @staticmethod - def create_app(foo): + def create_app(): return Flask('appname2') - assert find_best_app(Module) == Module.myapp + assert find_best_app(script_info, Module) == Module.myapp class Module: pass - pytest.raises(NoAppException, find_best_app, Module) + pytest.raises(NoAppException, find_best_app, script_info, Module) class Module: myapp1 = Flask('appname1') myapp2 = Flask('appname2') - pytest.raises(NoAppException, find_best_app, Module) + pytest.raises(NoAppException, find_best_app, script_info, Module) class Module: @staticmethod - def create_app(foo): + def create_app(foo, bar): return Flask('appname2') - pytest.raises(NoAppException, find_best_app, Module) + pytest.raises(NoAppException, find_best_app, script_info, Module) def test_prepare_exec_for_file(test_apps): @@ -117,13 +132,18 @@ def test_prepare_exec_for_file(test_apps): def test_locate_app(test_apps): """Test of locate_app.""" - assert locate_app("cliapp.app").name == "testapp" - assert locate_app("cliapp.app:testapp").name == "testapp" - assert locate_app("cliapp.multiapp:app1").name == "app1" - pytest.raises(NoAppException, locate_app, "notanpp.py") - pytest.raises(NoAppException, locate_app, "cliapp/app") - pytest.raises(RuntimeError, locate_app, "cliapp.app:notanapp") - pytest.raises(NoAppException, locate_app, "cliapp.importerrorapp") + script_info = ScriptInfo() + assert locate_app(script_info, "cliapp.app").name == "testapp" + assert locate_app(script_info, "cliapp.app:testapp").name == "testapp" + assert locate_app(script_info, "cliapp.multiapp:app1").name == "app1" + pytest.raises(NoAppException, locate_app, + script_info, "notanpp.py") + pytest.raises(NoAppException, locate_app, + script_info, "cliapp/app") + pytest.raises(RuntimeError, locate_app, + script_info, "cliapp.app:notanapp") + pytest.raises(NoAppException, locate_app, + script_info, "cliapp.importerrorapp") def test_find_default_import_path(test_apps, monkeypatch, tmpdir): From 5b0b9717da958fd6325c675d92be4f6667796112 Mon Sep 17 00:00:00 2001 From: Christian Stade-Schuldt Date: Tue, 23 May 2017 15:18:39 -0700 Subject: [PATCH 221/399] DRYing up the test suite using pytest fixtures (#2306) * add fixtures to conftest.py * use fixtures in test_appctx.py * use fixtures in test_blueprints.py * use fixtures in test_depreciations.py * use fixtures in test_regressions.py * use fixtures in test_reqctx.py * use fixtures in test_templating.py * use fixtures in test_user_error_handler.py * use fixtures in test_views.py * use fixtures in test_basics.py * use fixtures in test_helpers.py * use fixtures in test_testing.py * update conftest.py * make docstrings PEP-257 compliant * cleanup * switch dictonary format * use pytest parameterization for test_json_as_unicode --- tests/conftest.py | 52 ++- tests/test_appctx.py | 96 ++--- tests/test_basic.py | 487 +++++++++++------------- tests/test_blueprints.py | 280 ++++++++------ tests/test_deprecations.py | 13 +- tests/test_helpers.py | 615 +++++++++++++++---------------- tests/test_regression.py | 7 +- tests/test_reqctx.py | 51 +-- tests/test_templating.py | 210 ++++++----- tests/test_testing.py | 96 ++--- tests/test_user_error_handler.py | 22 +- tests/test_views.py | 44 ++- 12 files changed, 999 insertions(+), 974 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index eb130db6..40b1e88f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -13,6 +13,40 @@ import sys import pkgutil import pytest import textwrap +from flask import Flask as _Flask + + +class Flask(_Flask): + testing = True + secret_key = __name__ + + def make_response(self, rv): + if rv is None: + rv = '' + return super(Flask, self).make_response(rv) + + +@pytest.fixture +def app(): + app = Flask(__name__) + return app + + +@pytest.fixture +def app_ctx(app): + with app.app_context() as ctx: + yield ctx + + +@pytest.fixture +def req_ctx(app): + with app.test_request_context() as ctx: + yield ctx + + +@pytest.fixture +def client(app): + return app.test_client() @pytest.fixture @@ -63,12 +97,13 @@ def limit_loader(request, monkeypatch): def get_loader(*args, **kwargs): return LimitedLoader(old_get_loader(*args, **kwargs)) + monkeypatch.setattr(pkgutil, 'get_loader', get_loader) @pytest.fixture def modules_tmpdir(tmpdir, monkeypatch): - '''A tmpdir added to sys.path''' + """A tmpdir added to sys.path.""" rv = tmpdir.mkdir('modules_tmpdir') monkeypatch.syspath_prepend(str(rv)) return rv @@ -82,10 +117,10 @@ def modules_tmpdir_prefix(modules_tmpdir, monkeypatch): @pytest.fixture def site_packages(modules_tmpdir, monkeypatch): - '''Create a fake site-packages''' + """Create a fake site-packages.""" rv = modules_tmpdir \ - .mkdir('lib')\ - .mkdir('python{x[0]}.{x[1]}'.format(x=sys.version_info))\ + .mkdir('lib') \ + .mkdir('python{x[0]}.{x[1]}'.format(x=sys.version_info)) \ .mkdir('site-packages') monkeypatch.syspath_prepend(str(rv)) return rv @@ -93,8 +128,9 @@ def site_packages(modules_tmpdir, monkeypatch): @pytest.fixture def install_egg(modules_tmpdir, monkeypatch): - '''Generate egg from package name inside base and put the egg into - sys.path''' + """Generate egg from package name inside base and put the egg into + sys.path.""" + def inner(name, base=modules_tmpdir): if not isinstance(name, str): raise ValueError(name) @@ -118,6 +154,7 @@ def install_egg(modules_tmpdir, monkeypatch): egg_path, = modules_tmpdir.join('dist/').listdir() monkeypatch.syspath_prepend(str(egg_path)) return egg_path + return inner @@ -125,6 +162,7 @@ def install_egg(modules_tmpdir, monkeypatch): def purge_module(request): def inner(name): request.addfinalizer(lambda: sys.modules.pop(name, None)) + return inner @@ -132,4 +170,4 @@ def purge_module(request): def catch_deprecation_warnings(recwarn): yield gc.collect() - assert not recwarn.list + assert not recwarn.list, '\n'.join(str(w.message) for w in recwarn.list) diff --git a/tests/test_appctx.py b/tests/test_appctx.py index 13b61eee..7ef7b479 100644 --- a/tests/test_appctx.py +++ b/tests/test_appctx.py @@ -14,8 +14,7 @@ import pytest import flask -def test_basic_url_generation(): - app = flask.Flask(__name__) +def test_basic_url_generation(app): app.config['SERVER_NAME'] = 'localhost' app.config['PREFERRED_URL_SCHEME'] = 'https' @@ -27,31 +26,33 @@ def test_basic_url_generation(): rv = flask.url_for('index') assert rv == 'https://localhost/' -def test_url_generation_requires_server_name(): - app = flask.Flask(__name__) + +def test_url_generation_requires_server_name(app): with app.app_context(): with pytest.raises(RuntimeError): flask.url_for('index') + def test_url_generation_without_context_fails(): with pytest.raises(RuntimeError): flask.url_for('index') -def test_request_context_means_app_context(): - app = flask.Flask(__name__) + +def test_request_context_means_app_context(app): with app.test_request_context(): assert flask.current_app._get_current_object() == app assert flask._app_ctx_stack.top is None -def test_app_context_provides_current_app(): - app = flask.Flask(__name__) + +def test_app_context_provides_current_app(app): with app.app_context(): assert flask.current_app._get_current_object() == app assert flask._app_ctx_stack.top is None -def test_app_tearing_down(): + +def test_app_tearing_down(app): cleanup_stuff = [] - app = flask.Flask(__name__) + @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) @@ -61,9 +62,10 @@ def test_app_tearing_down(): assert cleanup_stuff == [None] -def test_app_tearing_down_with_previous_exception(): + +def test_app_tearing_down_with_previous_exception(app): cleanup_stuff = [] - app = flask.Flask(__name__) + @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) @@ -78,9 +80,10 @@ def test_app_tearing_down_with_previous_exception(): assert cleanup_stuff == [None] -def test_app_tearing_down_with_handled_exception(): + +def test_app_tearing_down_with_handled_exception(app): cleanup_stuff = [] - app = flask.Flask(__name__) + @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) @@ -93,46 +96,49 @@ def test_app_tearing_down_with_handled_exception(): assert cleanup_stuff == [None] -def test_app_ctx_globals_methods(): - app = flask.Flask(__name__) - with app.app_context(): - # get - assert flask.g.get('foo') is None - assert flask.g.get('foo', 'bar') == 'bar' - # __contains__ - assert 'foo' not in flask.g - flask.g.foo = 'bar' - assert 'foo' in flask.g - # setdefault - flask.g.setdefault('bar', 'the cake is a lie') - flask.g.setdefault('bar', 'hello world') - assert flask.g.bar == 'the cake is a lie' - # pop - assert flask.g.pop('bar') == 'the cake is a lie' - with pytest.raises(KeyError): - flask.g.pop('bar') - assert flask.g.pop('bar', 'more cake') == 'more cake' - # __iter__ - assert list(flask.g) == ['foo'] - -def test_custom_app_ctx_globals_class(): + +def test_app_ctx_globals_methods(app, app_ctx): + # get + assert flask.g.get('foo') is None + assert flask.g.get('foo', 'bar') == 'bar' + # __contains__ + assert 'foo' not in flask.g + flask.g.foo = 'bar' + assert 'foo' in flask.g + # setdefault + flask.g.setdefault('bar', 'the cake is a lie') + flask.g.setdefault('bar', 'hello world') + assert flask.g.bar == 'the cake is a lie' + # pop + assert flask.g.pop('bar') == 'the cake is a lie' + with pytest.raises(KeyError): + flask.g.pop('bar') + assert flask.g.pop('bar', 'more cake') == 'more cake' + # __iter__ + assert list(flask.g) == ['foo'] + + +def test_custom_app_ctx_globals_class(app): class CustomRequestGlobals(object): def __init__(self): self.spam = 'eggs' - app = flask.Flask(__name__) + app.app_ctx_globals_class = CustomRequestGlobals with app.app_context(): assert flask.render_template_string('{{ g.spam }}') == 'eggs' -def test_context_refcounts(): + +def test_context_refcounts(app, client): called = [] - app = flask.Flask(__name__) + @app.teardown_request def teardown_req(error=None): called.append('request') + @app.teardown_appcontext def teardown_app(error=None): called.append('app') + @app.route('/') def index(): with flask._app_ctx_stack.top: @@ -141,16 +147,16 @@ def test_context_refcounts(): env = flask._request_ctx_stack.top.request.environ assert env['werkzeug.request'] is not None return u'' - c = app.test_client() - res = c.get('/') + + res = client.get('/') assert res.status_code == 200 assert res.data == b'' assert called == ['request', 'app'] -def test_clean_pop(): +def test_clean_pop(app): + app.testing = False called = [] - app = flask.Flask(__name__) @app.teardown_request def teardown_req(error=None): @@ -166,5 +172,5 @@ def test_clean_pop(): except ZeroDivisionError: pass - assert called == ['test_appctx', 'TEARDOWN'] + assert called == ['conftest', 'TEARDOWN'] assert not flask.current_app diff --git a/tests/test_basic.py b/tests/test_basic.py index 54f4c8e6..9d72f6d1 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -25,20 +25,17 @@ from werkzeug.routing import BuildError import werkzeug.serving -def test_options_work(): - app = flask.Flask(__name__) - +def test_options_work(app, client): @app.route('/', methods=['GET', 'POST']) def index(): return 'Hello World' - rv = app.test_client().open('/', method='OPTIONS') + + rv = client.open('/', method='OPTIONS') assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] assert rv.data == b'' -def test_options_on_multiple_rules(): - app = flask.Flask(__name__) - +def test_options_on_multiple_rules(app, client): @app.route('/', methods=['GET', 'POST']) def index(): return 'Hello World' @@ -46,7 +43,8 @@ def test_options_on_multiple_rules(): @app.route('/', methods=['PUT']) def index_put(): return 'Aha!' - rv = app.test_client().open('/', method='OPTIONS') + + rv = client.open('/', method='OPTIONS') assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST', 'PUT'] @@ -55,6 +53,7 @@ def test_provide_automatic_options_attr(): def index(): return 'Hello World!' + index.provide_automatic_options = False app.route('/')(index) rv = app.test_client().open('/', method='OPTIONS') @@ -64,15 +63,14 @@ def test_provide_automatic_options_attr(): def index2(): return 'Hello World!' + index2.provide_automatic_options = True app.route('/', methods=['OPTIONS'])(index2) rv = app.test_client().open('/', method='OPTIONS') assert sorted(rv.allow) == ['OPTIONS'] -def test_provide_automatic_options_kwarg(): - app = flask.Flask(__name__) - +def test_provide_automatic_options_kwarg(app, client): def index(): return flask.request.method @@ -84,43 +82,39 @@ def test_provide_automatic_options_kwarg(): '/more', view_func=more, methods=['GET', 'POST'], provide_automatic_options=False ) + assert client.get('/').data == b'GET' - c = app.test_client() - assert c.get('/').data == b'GET' - - rv = c.post('/') + rv = client.post('/') assert rv.status_code == 405 assert sorted(rv.allow) == ['GET', 'HEAD'] # Older versions of Werkzeug.test.Client don't have an options method - if hasattr(c, 'options'): - rv = c.options('/') + if hasattr(client, 'options'): + rv = client.options('/') else: - rv = c.open('/', method='OPTIONS') + rv = client.open('/', method='OPTIONS') assert rv.status_code == 405 - rv = c.head('/') + rv = client.head('/') assert rv.status_code == 200 assert not rv.data # head truncates - assert c.post('/more').data == b'POST' - assert c.get('/more').data == b'GET' + assert client.post('/more').data == b'POST' + assert client.get('/more').data == b'GET' - rv = c.delete('/more') + rv = client.delete('/more') assert rv.status_code == 405 assert sorted(rv.allow) == ['GET', 'HEAD', 'POST'] - if hasattr(c, 'options'): - rv = c.options('/more') + if hasattr(client, 'options'): + rv = client.options('/more') else: - rv = c.open('/more', method='OPTIONS') + rv = client.open('/more', method='OPTIONS') assert rv.status_code == 405 -def test_request_dispatching(): - app = flask.Flask(__name__) - +def test_request_dispatching(app, client): @app.route('/') def index(): return flask.request.method @@ -129,32 +123,28 @@ def test_request_dispatching(): def more(): return flask.request.method - c = app.test_client() - assert c.get('/').data == b'GET' - rv = c.post('/') + assert client.get('/').data == b'GET' + rv = client.post('/') assert rv.status_code == 405 assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS'] - rv = c.head('/') + rv = client.head('/') assert rv.status_code == 200 assert not rv.data # head truncates - assert c.post('/more').data == b'POST' - assert c.get('/more').data == b'GET' - rv = c.delete('/more') + assert client.post('/more').data == b'POST' + assert client.get('/more').data == b'GET' + rv = client.delete('/more') assert rv.status_code == 405 assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] -def test_disallow_string_for_allowed_methods(): - app = flask.Flask(__name__) +def test_disallow_string_for_allowed_methods(app): with pytest.raises(TypeError): @app.route('/', methods='GET POST') def index(): return "Hey" -def test_url_mapping(): - app = flask.Flask(__name__) - +def test_url_mapping(app, client): random_uuid4 = "7eb41166-9ebf-4d26-b771-ea3f54f8b383" def index(): @@ -166,34 +156,31 @@ def test_url_mapping(): def options(): return random_uuid4 - app.add_url_rule('/', 'index', index) app.add_url_rule('/more', 'more', more, methods=['GET', 'POST']) # Issue 1288: Test that automatic options are not added when non-uppercase 'options' in methods app.add_url_rule('/options', 'options', options, methods=['options']) - c = app.test_client() - assert c.get('/').data == b'GET' - rv = c.post('/') + assert client.get('/').data == b'GET' + rv = client.post('/') assert rv.status_code == 405 assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS'] - rv = c.head('/') + rv = client.head('/') assert rv.status_code == 200 assert not rv.data # head truncates - assert c.post('/more').data == b'POST' - assert c.get('/more').data == b'GET' - rv = c.delete('/more') + assert client.post('/more').data == b'POST' + assert client.get('/more').data == b'GET' + rv = client.delete('/more') assert rv.status_code == 405 assert sorted(rv.allow) == ['GET', 'HEAD', 'OPTIONS', 'POST'] - rv = c.open('/options', method='OPTIONS') + rv = client.open('/options', method='OPTIONS') assert rv.status_code == 200 assert random_uuid4 in rv.data.decode("utf-8") -def test_werkzeug_routing(): +def test_werkzeug_routing(app, client): from werkzeug.routing import Submount, Rule - app = flask.Flask(__name__) app.url_map.add(Submount('/foo', [ Rule('/bar', endpoint='bar'), Rule('/', endpoint='index') @@ -204,17 +191,16 @@ def test_werkzeug_routing(): def index(): return 'index' + app.view_functions['bar'] = bar app.view_functions['index'] = index - c = app.test_client() - assert c.get('/foo/').data == b'index' - assert c.get('/foo/bar').data == b'bar' + assert client.get('/foo/').data == b'index' + assert client.get('/foo/bar').data == b'bar' -def test_endpoint_decorator(): +def test_endpoint_decorator(app, client): from werkzeug.routing import Submount, Rule - app = flask.Flask(__name__) app.url_map.add(Submount('/foo', [ Rule('/bar', endpoint='bar'), Rule('/', endpoint='index') @@ -228,13 +214,11 @@ def test_endpoint_decorator(): def index(): return 'index' - c = app.test_client() - assert c.get('/foo/').data == b'index' - assert c.get('/foo/bar').data == b'bar' + assert client.get('/foo/').data == b'index' + assert client.get('/foo/bar').data == b'bar' -def test_session(): - app = flask.Flask(__name__) +def test_session(app, client): app.secret_key = 'testkey' @app.route('/set', methods=['POST']) @@ -246,13 +230,11 @@ def test_session(): def get(): return flask.session['value'] - c = app.test_client() - assert c.post('/set', data={'value': '42'}).data == b'value set' - assert c.get('/get').data == b'42' + assert client.post('/set', data={'value': '42'}).data == b'value set' + assert client.get('/get').data == b'42' -def test_session_using_server_name(): - app = flask.Flask(__name__) +def test_session_using_server_name(app, client): app.config.update( SECRET_KEY='foo', SERVER_NAME='example.com' @@ -262,13 +244,13 @@ def test_session_using_server_name(): def index(): flask.session['testing'] = 42 return 'Hello World' - rv = app.test_client().get('/', 'http://example.com/') + + rv = client.get('/', 'http://example.com/') assert 'domain=.example.com' in rv.headers['set-cookie'].lower() assert 'httponly' in rv.headers['set-cookie'].lower() -def test_session_using_server_name_and_port(): - app = flask.Flask(__name__) +def test_session_using_server_name_and_port(app, client): app.config.update( SECRET_KEY='foo', SERVER_NAME='example.com:8080' @@ -278,13 +260,13 @@ def test_session_using_server_name_and_port(): def index(): flask.session['testing'] = 42 return 'Hello World' - rv = app.test_client().get('/', 'http://example.com:8080/') + + rv = client.get('/', 'http://example.com:8080/') assert 'domain=.example.com' in rv.headers['set-cookie'].lower() assert 'httponly' in rv.headers['set-cookie'].lower() -def test_session_using_server_name_port_and_path(): - app = flask.Flask(__name__) +def test_session_using_server_name_port_and_path(app, client): app.config.update( SECRET_KEY='foo', SERVER_NAME='example.com:8080', @@ -295,15 +277,15 @@ def test_session_using_server_name_port_and_path(): def index(): flask.session['testing'] = 42 return 'Hello World' - rv = app.test_client().get('/', 'http://example.com:8080/foo') + + rv = client.get('/', 'http://example.com:8080/foo') assert 'domain=example.com' in rv.headers['set-cookie'].lower() assert 'path=/foo' in rv.headers['set-cookie'].lower() assert 'httponly' in rv.headers['set-cookie'].lower() -def test_session_using_application_root(): +def test_session_using_application_root(app, client): class PrefixPathMiddleware(object): - def __init__(self, app, prefix): self.app = app self.prefix = prefix @@ -312,7 +294,6 @@ def test_session_using_application_root(): environ['SCRIPT_NAME'] = self.prefix return self.app(environ, start_response) - app = flask.Flask(__name__) app.wsgi_app = PrefixPathMiddleware(app.wsgi_app, '/bar') app.config.update( SECRET_KEY='foo', @@ -323,12 +304,12 @@ def test_session_using_application_root(): def index(): flask.session['testing'] = 42 return 'Hello World' - rv = app.test_client().get('/', 'http://example.com:8080/') + + rv = client.get('/', 'http://example.com:8080/') assert 'path=/bar' in rv.headers['set-cookie'].lower() -def test_session_using_session_settings(): - app = flask.Flask(__name__) +def test_session_using_session_settings(app, client): app.config.update( SECRET_KEY='foo', SERVER_NAME='www.example.com:8080', @@ -343,7 +324,8 @@ def test_session_using_session_settings(): def index(): flask.session['testing'] = 42 return 'Hello World' - rv = app.test_client().get('/', 'http://www.example.com:8080/test/') + + rv = client.get('/', 'http://www.example.com:8080/test/') cookie = rv.headers['set-cookie'].lower() assert 'domain=.example.com' in cookie assert 'path=/' in cookie @@ -351,8 +333,7 @@ def test_session_using_session_settings(): assert 'httponly' not in cookie -def test_session_localhost_warning(recwarn): - app = flask.Flask(__name__) +def test_session_localhost_warning(recwarn, app, client): app.config.update( SECRET_KEY='testing', SERVER_NAME='localhost:5000', @@ -363,14 +344,13 @@ def test_session_localhost_warning(recwarn): flask.session['testing'] = 42 return 'testing' - rv = app.test_client().get('/', 'http://localhost:5000/') + rv = client.get('/', 'http://localhost:5000/') assert 'domain' not in rv.headers['set-cookie'].lower() w = recwarn.pop(UserWarning) assert '"localhost" is not a valid cookie domain' in str(w.message) -def test_session_ip_warning(recwarn): - app = flask.Flask(__name__) +def test_session_ip_warning(recwarn, app, client): app.config.update( SECRET_KEY='testing', SERVER_NAME='127.0.0.1:5000', @@ -381,7 +361,7 @@ def test_session_ip_warning(recwarn): flask.session['testing'] = 42 return 'testing' - rv = app.test_client().get('/', 'http://127.0.0.1:5000/') + rv = client.get('/', 'http://127.0.0.1:5000/') assert 'domain=127.0.0.1' in rv.headers['set-cookie'].lower() w = recwarn.pop(UserWarning) assert 'cookie domain is an IP' in str(w.message) @@ -393,15 +373,15 @@ def test_missing_session(): def expect_exception(f, *args, **kwargs): e = pytest.raises(RuntimeError, f, *args, **kwargs) assert e.value.args and 'session is unavailable' in e.value.args[0] + with app.test_request_context(): assert flask.session.get('missing_key') is None expect_exception(flask.session.__setitem__, 'foo', 42) expect_exception(flask.session.pop, 'foo') -def test_session_expiration(): +def test_session_expiration(app, client): permanent = True - app = flask.Flask(__name__) app.secret_key = 'testkey' @app.route('/') @@ -414,7 +394,6 @@ def test_session_expiration(): def test(): return text_type(flask.session.permanent) - client = app.test_client() rv = client.get('/') assert 'set-cookie' in rv.headers match = re.search(r'(?i)\bexpires=([^;]+)', rv.headers['set-cookie']) @@ -428,16 +407,14 @@ def test_session_expiration(): assert rv.data == b'True' permanent = False - rv = app.test_client().get('/') + rv = client.get('/') assert 'set-cookie' in rv.headers match = re.search(r'\bexpires=([^;]+)', rv.headers['set-cookie']) assert match is None -def test_session_stored_last(): - app = flask.Flask(__name__) +def test_session_stored_last(app, client): app.secret_key = 'development-key' - app.testing = True @app.after_request def modify_session(response): @@ -448,15 +425,12 @@ def test_session_stored_last(): def dump_session_contents(): return repr(flask.session.get('foo')) - c = app.test_client() - assert c.get('/').data == b'None' - assert c.get('/').data == b'42' + assert client.get('/').data == b'None' + assert client.get('/').data == b'42' -def test_session_special_types(): - app = flask.Flask(__name__) +def test_session_special_types(app, client): app.secret_key = 'development-key' - app.testing = True now = datetime.utcnow().replace(microsecond=0) the_uuid = uuid.uuid4() @@ -473,9 +447,8 @@ def test_session_special_types(): def dump_session_contents(): return pickle.dumps(dict(flask.session)) - c = app.test_client() - c.get('/') - rv = pickle.loads(c.get('/').data) + client.get('/') + rv = pickle.loads(client.get('/').data) assert rv['m'] == flask.Markup('Hello!') assert type(rv['m']) == flask.Markup assert rv['dt'] == now @@ -485,9 +458,7 @@ def test_session_special_types(): assert rv['t'] == (1, 2, 3) -def test_session_cookie_setting(): - app = flask.Flask(__name__) - app.testing = True +def test_session_cookie_setting(app): app.secret_key = 'dev key' is_permanent = True @@ -529,8 +500,7 @@ def test_session_cookie_setting(): run_test(expect_header=False) -def test_session_vary_cookie(): - app = flask.Flask(__name__) +def test_session_vary_cookie(app, client): app.secret_key = 'testkey' @app.route('/set') @@ -554,10 +524,8 @@ def test_session_vary_cookie(): def no_vary_header(): return '' - c = app.test_client() - def expect(path, header=True): - rv = c.get(path) + rv = client.get(path) if header: assert rv.headers['Vary'] == 'Cookie' @@ -571,29 +539,25 @@ def test_session_vary_cookie(): expect('/no-vary-header', False) -def test_flashes(): - app = flask.Flask(__name__) +def test_flashes(app, req_ctx): app.secret_key = 'testkey' - with app.test_request_context(): - assert not flask.session.modified - flask.flash('Zap') - flask.session.modified = False - flask.flash('Zip') - assert flask.session.modified - assert list(flask.get_flashed_messages()) == ['Zap', 'Zip'] + assert not flask.session.modified + flask.flash('Zap') + flask.session.modified = False + flask.flash('Zip') + assert flask.session.modified + assert list(flask.get_flashed_messages()) == ['Zap', 'Zip'] -def test_extended_flashing(): +def test_extended_flashing(app): # Be sure app.testing=True below, else tests can fail silently. # # Specifically, if app.testing is not set to True, the AssertionErrors # in the view functions will cause a 500 response to the test client # instead of propagating exceptions. - app = flask.Flask(__name__) app.secret_key = 'testkey' - app.testing = True @app.route('/') def index(): @@ -651,29 +615,24 @@ def test_extended_flashing(): # Create new test client on each test to clean flashed messages. - c = app.test_client() - c.get('/') - c.get('/test/') - - c = app.test_client() - c.get('/') - c.get('/test_with_categories/') + client = app.test_client() + client.get('/') + client.get('/test_with_categories/') - c = app.test_client() - c.get('/') - c.get('/test_filter/') + client = app.test_client() + client.get('/') + client.get('/test_filter/') - c = app.test_client() - c.get('/') - c.get('/test_filters/') + client = app.test_client() + client.get('/') + client.get('/test_filters/') - c = app.test_client() - c.get('/') - c.get('/test_filters_without_returning_categories/') + client = app.test_client() + client.get('/') + client.get('/test_filters_without_returning_categories/') -def test_request_processing(): - app = flask.Flask(__name__) +def test_request_processing(app, client): evts = [] @app.before_request @@ -691,14 +650,14 @@ def test_request_processing(): assert 'before' in evts assert 'after' not in evts return 'request' + assert 'after' not in evts - rv = app.test_client().get('/').data + rv = client.get('/').data assert 'after' in evts assert rv == b'request|after' -def test_request_preprocessing_early_return(): - app = flask.Flask(__name__) +def test_request_preprocessing_early_return(app, client): evts = [] @app.before_request @@ -720,31 +679,28 @@ def test_request_preprocessing_early_return(): evts.append('index') return "damnit" - rv = app.test_client().get('/').data.strip() + rv = client.get('/').data.strip() assert rv == b'hello' assert evts == [1, 2] -def test_after_request_processing(): - app = flask.Flask(__name__) - app.testing = True - +def test_after_request_processing(app, client): @app.route('/') def index(): @flask.after_this_request def foo(response): response.headers['X-Foo'] = 'a header' return response + return 'Test' - c = app.test_client() - resp = c.get('/') + + resp = client.get('/') assert resp.status_code == 200 assert resp.headers['X-Foo'] == 'a header' -def test_teardown_request_handler(): +def test_teardown_request_handler(app, client): called = [] - app = flask.Flask(__name__) @app.teardown_request def teardown_request(exc): @@ -754,16 +710,15 @@ def test_teardown_request_handler(): @app.route('/') def root(): return "Response" - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.status_code == 200 assert b'Response' in rv.data assert len(called) == 1 -def test_teardown_request_handler_debug_mode(): +def test_teardown_request_handler_debug_mode(app, client): called = [] - app = flask.Flask(__name__) - app.testing = True @app.teardown_request def teardown_request(exc): @@ -773,16 +728,17 @@ def test_teardown_request_handler_debug_mode(): @app.route('/') def root(): return "Response" - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.status_code == 200 assert b'Response' in rv.data assert len(called) == 1 -def test_teardown_request_handler_error(): +def test_teardown_request_handler_error(app, client): called = [] - app = flask.Flask(__name__) app.config['LOGGER_HANDLER_POLICY'] = 'never' + app.testing = False @app.teardown_request def teardown_request1(exc): @@ -811,15 +767,15 @@ def test_teardown_request_handler_error(): @app.route('/') def fails(): 1 // 0 - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.status_code == 500 assert b'Internal Server Error' in rv.data assert len(called) == 2 -def test_before_after_request_order(): +def test_before_after_request_order(app, client): called = [] - app = flask.Flask(__name__) @app.before_request def before1(): @@ -850,14 +806,15 @@ def test_before_after_request_order(): @app.route('/') def index(): return '42' - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.data == b'42' assert called == [1, 2, 3, 4, 5, 6] -def test_error_handling(): - app = flask.Flask(__name__) +def test_error_handling(app, client): app.config['LOGGER_HANDLER_POLICY'] = 'never' + app.testing = False @app.errorhandler(404) def not_found(e): @@ -882,21 +839,21 @@ def test_error_handling(): @app.route('/forbidden') def error2(): flask.abort(403) - c = app.test_client() - rv = c.get('/') + + rv = client.get('/') assert rv.status_code == 404 assert rv.data == b'not found' - rv = c.get('/error') + rv = client.get('/error') assert rv.status_code == 500 assert b'internal server error' == rv.data - rv = c.get('/forbidden') + rv = client.get('/forbidden') assert rv.status_code == 403 assert b'forbidden' == rv.data -def test_error_handling_processing(): - app = flask.Flask(__name__) +def test_error_handling_processing(app, client): app.config['LOGGER_HANDLER_POLICY'] = 'never' + app.testing = False @app.errorhandler(500) def internal_server_error(e): @@ -911,32 +868,28 @@ def test_error_handling_processing(): resp.mimetype = 'text/x-special' return resp - with app.test_client() as c: - resp = c.get('/') - assert resp.mimetype == 'text/x-special' - assert resp.data == b'internal server error' + resp = client.get('/') + assert resp.mimetype == 'text/x-special' + assert resp.data == b'internal server error' -def test_baseexception_error_handling(): - app = flask.Flask(__name__) +def test_baseexception_error_handling(app, client): app.config['LOGGER_HANDLER_POLICY'] = 'never' + app.testing = False @app.route('/') def broken_func(): raise KeyboardInterrupt() - with app.test_client() as c: - with pytest.raises(KeyboardInterrupt): - c.get('/') + with pytest.raises(KeyboardInterrupt): + client.get('/') ctx = flask._request_ctx_stack.top assert ctx.preserved assert type(ctx._preserved_exc) is KeyboardInterrupt -def test_before_request_and_routing_errors(): - app = flask.Flask(__name__) - +def test_before_request_and_routing_errors(app, client): @app.before_request def attach_something(): flask.g.something = 'value' @@ -944,17 +897,16 @@ def test_before_request_and_routing_errors(): @app.errorhandler(404) def return_something(error): return flask.g.something, 404 - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.status_code == 404 assert rv.data == b'value' -def test_user_error_handling(): +def test_user_error_handling(app, client): class MyException(Exception): pass - app = flask.Flask(__name__) - @app.errorhandler(MyException) def handle_my_exception(e): assert isinstance(e, MyException) @@ -964,16 +916,13 @@ def test_user_error_handling(): def index(): raise MyException() - c = app.test_client() - assert c.get('/').data == b'42' + assert client.get('/').data == b'42' -def test_http_error_subclass_handling(): +def test_http_error_subclass_handling(app, client): class ForbiddenSubclass(Forbidden): pass - app = flask.Flask(__name__) - @app.errorhandler(ForbiddenSubclass) def handle_forbidden_subclass(e): assert isinstance(e, ForbiddenSubclass) @@ -997,19 +946,16 @@ def test_http_error_subclass_handling(): def index3(): raise Forbidden() - c = app.test_client() - assert c.get('/1').data == b'banana' - assert c.get('/2').data == b'apple' - assert c.get('/3').data == b'apple' + assert client.get('/1').data == b'banana' + assert client.get('/2').data == b'apple' + assert client.get('/3').data == b'apple' -def test_trapping_of_bad_request_key_errors(): - app = flask.Flask(__name__) - app.testing = True - +def test_trapping_of_bad_request_key_errors(app): @app.route('/fail') def fail(): flask.request.form['missing_key'] + c = app.test_client() assert c.get('/fail').status_code == 400 @@ -1020,23 +966,19 @@ def test_trapping_of_bad_request_key_errors(): assert e.errisinstance(BadRequest) -def test_trapping_of_all_http_exceptions(): - app = flask.Flask(__name__) - app.testing = True +def test_trapping_of_all_http_exceptions(app, client): app.config['TRAP_HTTP_EXCEPTIONS'] = True @app.route('/fail') def fail(): flask.abort(404) - c = app.test_client() with pytest.raises(NotFound): - c.get('/fail') + client.get('/fail') -def test_enctype_debug_helper(): +def test_enctype_debug_helper(app, client): from flask.debughelpers import DebugFilesKeyError - app = flask.Flask(__name__) app.debug = True @app.route('/fail', methods=['POST']) @@ -1046,7 +988,7 @@ def test_enctype_debug_helper(): # with statement is important because we leave an exception on the # stack otherwise and we want to ensure that this is not the case # to not negatively affect other tests. - with app.test_client() as c: + with client as c: with pytest.raises(DebugFilesKeyError) as e: c.post('/fail', data={'foo': 'index.txt'}) assert 'no file contents were transmitted' in str(e.value) @@ -1230,7 +1172,7 @@ def test_jsonify_no_prettyprint(): "submsg": "W00t" }, "msg2": "foobar" - } + } rv = flask.make_response( flask.jsonify(uncompressed_msg), 200) @@ -1241,8 +1183,8 @@ def test_jsonify_prettyprint(): app = flask.Flask(__name__) app.config.update({"JSONIFY_PRETTYPRINT_REGULAR": True}) with app.test_request_context(): - compressed_msg = {"msg":{"submsg":"W00t"},"msg2":"foobar"} - pretty_response =\ + compressed_msg = {"msg": {"submsg": "W00t"}, "msg2": "foobar"} + pretty_response = \ b'{\n "msg": {\n "submsg": "W00t"\n }, \n "msg2": "foobar"\n}\n' rv = flask.make_response( @@ -1276,10 +1218,11 @@ def test_url_generation(): @app.route('/hello/', methods=['POST']) def hello(): pass + with app.test_request_context(): assert flask.url_for('hello', name='test x') == '/hello/test%20x' assert flask.url_for('hello', name='test x', _external=True) == \ - 'http://localhost/hello/test%20x' + 'http://localhost/hello/test%20x' def test_build_error_handler(): @@ -1305,6 +1248,7 @@ def test_build_error_handler(): def handler(error, endpoint, values): # Just a test. return '/test_handler/' + app.url_build_error_handlers.append(handler) with app.test_request_context(): assert flask.url_for('spam') == '/test_handler/' @@ -1316,6 +1260,7 @@ def test_build_error_handler_reraise(): # Test a custom handler which reraises the BuildError def handler_raises_build_error(error, endpoint, values): raise error + app.url_build_error_handlers.append(handler_raises_build_error) with app.test_request_context(): @@ -1343,19 +1288,20 @@ def test_custom_converters(): from werkzeug.routing import BaseConverter class ListConverter(BaseConverter): - def to_python(self, value): return value.split(',') def to_url(self, value): base_to_url = super(ListConverter, self).to_url return ','.join(base_to_url(x) for x in value) + app = flask.Flask(__name__) app.url_map.converters['list'] = ListConverter @app.route('/') def index(args): return '|'.join(args) + c = app.test_client() assert c.get('/1,2,3').data == b'1|2|3' @@ -1368,7 +1314,7 @@ def test_static_files(): assert rv.data.strip() == b'

    Hello World!

    ' with app.test_request_context(): assert flask.url_for('static', filename='index.html') == \ - '/static/index.html' + '/static/index.html' rv.close() @@ -1403,8 +1349,8 @@ def test_static_route_with_host_matching(): assert rv.status_code == 200 rv.close() with app.test_request_context(): - rv = flask.url_for('static', filename='index.html', _external=True) - assert rv == 'http://example.com/static/index.html' + rv = flask.url_for('static', filename='index.html', _external=True) + assert rv == 'http://example.com/static/index.html' # Providing static_host without host_matching=True should error. with pytest.raises(Exception): flask.Flask(__name__, static_host='example.com') @@ -1485,6 +1431,7 @@ def test_exception_propagation(): @app.route('/') def index(): 1 // 0 + c = app.test_client() if config_key is not None: app.config[config_key] = True @@ -1550,7 +1497,7 @@ def test_url_processors(): @app.url_defaults def add_language_code(endpoint, values): if flask.g.lang_code is not None and \ - app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): + app.url_map.is_endpoint_expecting(endpoint, 'lang_code'): values.setdefault('lang_code', flask.g.lang_code) @app.url_value_preprocessor @@ -1622,6 +1569,7 @@ def test_debug_mode_complains_after_first_request(): @app.route('/') def index(): return 'Awesome' + assert not app.got_first_request assert app.test_client().get('/').data == b'Awesome' with pytest.raises(AssertionError) as e: @@ -1635,6 +1583,7 @@ def test_debug_mode_complains_after_first_request(): @app.route('/foo') def working(): return 'Meh' + assert app.test_client().get('/foo').data == b'Meh' assert app.got_first_request @@ -1646,6 +1595,7 @@ def test_before_first_request_functions(): @app.before_first_request def foo(): got.append(42) + c = app.test_client() c.get('/') assert got == [42] @@ -1683,6 +1633,7 @@ def test_routing_redirect_debugging(): @app.route('/foo/', methods=['GET', 'POST']) def foo(): return 'success' + with app.test_client() as c: with pytest.raises(AssertionError) as e: c.post('/foo', data={}) @@ -1747,8 +1698,7 @@ def test_preserve_only_once(): assert flask._app_ctx_stack.top is None -def test_preserve_remembers_exception(): - app = flask.Flask(__name__) +def test_preserve_remembers_exception(app, client): app.debug = True errors = [] @@ -1764,51 +1714,40 @@ def test_preserve_remembers_exception(): def teardown_handler(exc): errors.append(exc) - c = app.test_client() - # After this failure we did not yet call the teardown handler with pytest.raises(ZeroDivisionError): - c.get('/fail') + client.get('/fail') assert errors == [] # But this request triggers it, and it's an error - c.get('/success') + client.get('/success') assert len(errors) == 2 assert isinstance(errors[0], ZeroDivisionError) # At this point another request does nothing. - c.get('/success') + client.get('/success') assert len(errors) == 3 assert errors[1] is None -def test_get_method_on_g(): - app = flask.Flask(__name__) - app.testing = True - - with app.app_context(): - assert flask.g.get('x') is None - assert flask.g.get('x', 11) == 11 - flask.g.x = 42 - assert flask.g.get('x') == 42 - assert flask.g.x == 42 - +def test_get_method_on_g(app_ctx): + assert flask.g.get('x') is None + assert flask.g.get('x', 11) == 11 + flask.g.x = 42 + assert flask.g.get('x') == 42 + assert flask.g.x == 42 -def test_g_iteration_protocol(): - app = flask.Flask(__name__) - app.testing = True - with app.app_context(): - flask.g.foo = 23 - flask.g.bar = 42 - assert 'foo' in flask.g - assert 'foos' not in flask.g - assert sorted(flask.g) == ['bar', 'foo'] +def test_g_iteration_protocol(app_ctx): + flask.g.foo = 23 + flask.g.bar = 42 + assert 'foo' in flask.g + assert 'foos' not in flask.g + assert sorted(flask.g) == ['bar', 'foo'] -def test_subdomain_basic_support(): - app = flask.Flask(__name__) - app.config['SERVER_NAME'] = 'localhost' +def test_subdomain_basic_support(app, client): + app.config['SERVER_NAME'] = 'localhost.localdomain' @app.route('/') def normal_index(): @@ -1818,57 +1757,49 @@ def test_subdomain_basic_support(): def test_index(): return 'test index' - c = app.test_client() - rv = c.get('/', 'http://localhost/') + rv = client.get('/', 'http://localhost.localdomain/') assert rv.data == b'normal index' - rv = c.get('/', 'http://test.localhost/') + rv = client.get('/', 'http://test.localhost.localdomain/') assert rv.data == b'test index' -def test_subdomain_matching(): - app = flask.Flask(__name__) - app.config['SERVER_NAME'] = 'localhost' +def test_subdomain_matching(app, client): + app.config['SERVER_NAME'] = 'localhost.localdomain' @app.route('/', subdomain='') def index(user): return 'index for %s' % user - c = app.test_client() - rv = c.get('/', 'http://mitsuhiko.localhost/') + rv = client.get('/', 'http://mitsuhiko.localhost.localdomain/') assert rv.data == b'index for mitsuhiko' -def test_subdomain_matching_with_ports(): - app = flask.Flask(__name__) - app.config['SERVER_NAME'] = 'localhost:3000' +def test_subdomain_matching_with_ports(app, client): + app.config['SERVER_NAME'] = 'localhost.localdomain:3000' @app.route('/', subdomain='') def index(user): return 'index for %s' % user - c = app.test_client() - rv = c.get('/', 'http://mitsuhiko.localhost:3000/') + rv = client.get('/', 'http://mitsuhiko.localhost.localdomain:3000/') assert rv.data == b'index for mitsuhiko' -def test_multi_route_rules(): - app = flask.Flask(__name__) - +def test_multi_route_rules(app, client): @app.route('/') @app.route('//') def index(test='a'): return test - rv = app.test_client().open('/') + rv = client.open('/') assert rv.data == b'a' - rv = app.test_client().open('/b/') + rv = client.open('/b/') assert rv.data == b'b' -def test_multi_route_class_views(): +def test_multi_route_class_views(app, client): class View(object): - def __init__(self, app): app.add_url_rule('/', 'index', self.index) app.add_url_rule('//', 'index', self.index) @@ -1876,35 +1807,32 @@ def test_multi_route_class_views(): def index(self, test='a'): return test - app = flask.Flask(__name__) _ = View(app) - rv = app.test_client().open('/') + rv = client.open('/') assert rv.data == b'a' - rv = app.test_client().open('/b/') + rv = client.open('/b/') assert rv.data == b'b' -def test_run_defaults(monkeypatch): +def test_run_defaults(monkeypatch, app): rv = {} # Mocks werkzeug.serving.run_simple method def run_simple_mock(*args, **kwargs): rv['result'] = 'running...' - app = flask.Flask(__name__) monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) app.run() assert rv['result'] == 'running...' -def test_run_server_port(monkeypatch): +def test_run_server_port(monkeypatch, app): rv = {} # Mocks werkzeug.serving.run_simple method def run_simple_mock(hostname, port, application, *args, **kwargs): rv['result'] = 'running on %s:%s ...' % (hostname, port) - app = flask.Flask(__name__) monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) hostname, port = 'localhost', 8000 app.run(hostname, port, debug=True) @@ -1912,17 +1840,16 @@ def test_run_server_port(monkeypatch): @pytest.mark.parametrize('host,port,expect_host,expect_port', ( - (None, None, 'pocoo.org', 8080), - ('localhost', None, 'localhost', 8080), - (None, 80, 'pocoo.org', 80), - ('localhost', 80, 'localhost', 80), + (None, None, 'pocoo.org', 8080), + ('localhost', None, 'localhost', 8080), + (None, 80, 'pocoo.org', 80), + ('localhost', 80, 'localhost', 80), )) -def test_run_from_config(monkeypatch, host, port, expect_host, expect_port): +def test_run_from_config(monkeypatch, host, port, expect_host, expect_port, app): def run_simple_mock(hostname, port, *args, **kwargs): assert hostname == expect_host assert port == expect_port monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) - app = flask.Flask(__name__) app.config['SERVER_NAME'] = 'pocoo.org:8080' app.run(host, port) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index 5c5119c0..434fca37 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -18,7 +18,7 @@ from werkzeug.http import parse_cache_control_header from jinja2 import TemplateNotFound -def test_blueprint_specific_error_handling(): +def test_blueprint_specific_error_handling(app, client): frontend = flask.Blueprint('frontend', __name__) backend = flask.Blueprint('backend', __name__) sideend = flask.Blueprint('sideend', __name__) @@ -43,7 +43,6 @@ def test_blueprint_specific_error_handling(): def sideend_no(): flask.abort(403) - app = flask.Flask(__name__) app.register_blueprint(frontend) app.register_blueprint(backend) app.register_blueprint(sideend) @@ -52,15 +51,15 @@ def test_blueprint_specific_error_handling(): def app_forbidden(e): return 'application itself says no', 403 - c = app.test_client() + assert client.get('/frontend-no').data == b'frontend says no' + assert client.get('/backend-no').data == b'backend says no' + assert client.get('/what-is-a-sideend').data == b'application itself says no' - assert c.get('/frontend-no').data == b'frontend says no' - assert c.get('/backend-no').data == b'backend says no' - assert c.get('/what-is-a-sideend').data == b'application itself says no' -def test_blueprint_specific_user_error_handling(): +def test_blueprint_specific_user_error_handling(app, client): class MyDecoratorException(Exception): pass + class MyFunctionException(Exception): pass @@ -74,32 +73,30 @@ def test_blueprint_specific_user_error_handling(): def my_function_exception_handler(e): assert 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() + assert client.get('/decorator').data == b'boom' + assert client.get('/function').data == b'bam' - assert c.get('/decorator').data == b'boom' - assert c.get('/function').data == b'bam' -def test_blueprint_app_error_handling(): +def test_blueprint_app_error_handling(app, client): errors = flask.Blueprint('errors', __name__) @errors.app_errorhandler(403) def forbidden_handler(e): return 'you shall not pass', 403 - app = flask.Flask(__name__) - @app.route('/forbidden') def app_forbidden(): flask.abort(403) @@ -113,12 +110,11 @@ def test_blueprint_app_error_handling(): app.register_blueprint(errors) app.register_blueprint(forbidden_bp) - c = app.test_client() + assert client.get('/forbidden').data == b'you shall not pass' + assert client.get('/nope').data == b'you shall not pass' - assert c.get('/forbidden').data == b'you shall not pass' - assert c.get('/nope').data == b'you shall not pass' -def test_blueprint_url_definitions(): +def test_blueprint_url_definitions(app, client): bp = flask.Blueprint('test', __name__) @bp.route('/foo', defaults={'baz': 42}) @@ -129,17 +125,16 @@ def test_blueprint_url_definitions(): def bar(bar): return text_type(bar) - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/1', url_defaults={'bar': 23}) app.register_blueprint(bp, url_prefix='/2', url_defaults={'bar': 19}) - c = app.test_client() - assert c.get('/1/foo').data == b'23/42' - assert c.get('/2/foo').data == b'19/42' - assert c.get('/1/bar').data == b'23' - assert c.get('/2/bar').data == b'19' + assert client.get('/1/foo').data == b'23/42' + assert client.get('/2/foo').data == b'19/42' + assert client.get('/1/bar').data == b'23' + assert client.get('/2/bar').data == b'19' -def test_blueprint_url_processors(): + +def test_blueprint_url_processors(app, client): bp = flask.Blueprint('frontend', __name__, url_prefix='/') @bp.url_defaults @@ -158,28 +153,26 @@ def test_blueprint_url_processors(): def about(): return flask.url_for('.index') - app = flask.Flask(__name__) app.register_blueprint(bp) - c = app.test_client() + assert client.get('/de/').data == b'/de/about' + assert client.get('/de/about').data == b'/de/' - assert c.get('/de/').data == b'/de/about' - assert c.get('/de/about').data == b'/de/' def test_templates_and_static(test_apps): from blueprintapp import app - c = app.test_client() + client = app.test_client() - rv = c.get('/') + rv = client.get('/') assert rv.data == b'Hello from the Frontend' - rv = c.get('/admin/') + rv = client.get('/admin/') assert rv.data == b'Hello from the Admin' - rv = c.get('/admin/index2') + rv = client.get('/admin/index2') assert rv.data == b'Hello from the Admin' - rv = c.get('/admin/static/test.txt') + rv = client.get('/admin/static/test.txt') assert rv.data.strip() == b'Admin File' rv.close() - rv = c.get('/admin/static/css/test.css') + rv = client.get('/admin/static/css/test.css') assert rv.data.strip() == b'/* nested file */' rv.close() @@ -190,7 +183,7 @@ def test_templates_and_static(test_apps): if app.config['SEND_FILE_MAX_AGE_DEFAULT'] == expected_max_age: expected_max_age = 7200 app.config['SEND_FILE_MAX_AGE_DEFAULT'] = expected_max_age - rv = c.get('/admin/static/css/test.css') + rv = client.get('/admin/static/css/test.css') cc = parse_cache_control_header(rv.headers['Cache-Control']) assert cc.max_age == expected_max_age rv.close() @@ -208,8 +201,10 @@ def test_templates_and_static(test_apps): with flask.Flask(__name__).test_request_context(): assert flask.render_template('nested/nested.txt') == 'I\'m nested' + def test_default_static_cache_timeout(): app = flask.Flask(__name__) + class MyBlueprint(flask.Blueprint): def get_send_file_max_age(self, filename): return 100 @@ -232,12 +227,14 @@ def test_default_static_cache_timeout(): finally: app.config['SEND_FILE_MAX_AGE_DEFAULT'] = max_age_default + def test_templates_list(test_apps): from blueprintapp import app templates = sorted(app.jinja_env.list_templates()) assert templates == ['admin/index.html', 'frontend/index.html'] -def test_dotted_names(): + +def test_dotted_names(app, client): frontend = flask.Blueprint('myapp.frontend', __name__) backend = flask.Blueprint('myapp.backend', __name__) @@ -253,18 +250,15 @@ def test_dotted_names(): def backend_index(): return flask.url_for('myapp.frontend.frontend_index') - app = flask.Flask(__name__) app.register_blueprint(frontend) app.register_blueprint(backend) - c = app.test_client() - assert c.get('/fe').data.strip() == b'/be' - assert c.get('/fe2').data.strip() == b'/fe' - assert c.get('/be').data.strip() == b'/fe' + assert client.get('/fe').data.strip() == b'/be' + assert client.get('/fe2').data.strip() == b'/fe' + assert client.get('/be').data.strip() == b'/fe' -def test_dotted_names_from_app(): - app = flask.Flask(__name__) - app.testing = True + +def test_dotted_names_from_app(app, client): test = flask.Blueprint('test', __name__) @app.route('/') @@ -277,11 +271,11 @@ def test_dotted_names_from_app(): app.register_blueprint(test) - with app.test_client() as c: - rv = c.get('/') - assert rv.data == b'/test/' + rv = client.get('/') + assert rv.data == b'/test/' -def test_empty_url_defaults(): + +def test_empty_url_defaults(app, client): bp = flask.Blueprint('bp', __name__) @bp.route('/', defaults={'page': 1}) @@ -289,15 +283,13 @@ def test_empty_url_defaults(): def something(page): return str(page) - app = flask.Flask(__name__) app.register_blueprint(bp) - c = app.test_client() - assert c.get('/').data == b'1' - assert c.get('/page/2').data == b'2' + assert client.get('/').data == b'1' + assert client.get('/page/2').data == b'2' -def test_route_decorator_custom_endpoint(): +def test_route_decorator_custom_endpoint(app, client): bp = flask.Blueprint('bp', __name__) @bp.route('/foo') @@ -316,21 +308,20 @@ def test_route_decorator_custom_endpoint(): def bar_foo(): return flask.request.endpoint - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') @app.route('/') def index(): return flask.request.endpoint - c = app.test_client() - assert c.get('/').data == b'index' - assert c.get('/py/foo').data == b'bp.foo' - assert c.get('/py/bar').data == b'bp.bar' - assert c.get('/py/bar/123').data == b'bp.123' - assert c.get('/py/bar/foo').data == b'bp.bar_foo' + assert client.get('/').data == b'index' + assert client.get('/py/foo').data == b'bp.foo' + assert client.get('/py/bar').data == b'bp.bar' + assert client.get('/py/bar/123').data == b'bp.123' + assert client.get('/py/bar/foo').data == b'bp.bar_foo' + -def test_route_decorator_custom_endpoint_with_dots(): +def test_route_decorator_custom_endpoint_with_dots(app, client): bp = flask.Blueprint('bp', __name__) @bp.route('/foo') @@ -371,21 +362,18 @@ def test_route_decorator_custom_endpoint_with_dots(): lambda: None ) - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') - c = app.test_client() - assert c.get('/py/foo').data == b'bp.foo' + assert client.get('/py/foo').data == b'bp.foo' # The rule's didn't actually made it through - rv = c.get('/py/bar') + rv = client.get('/py/bar') assert rv.status_code == 404 - rv = c.get('/py/bar/123') + rv = client.get('/py/bar/123') assert rv.status_code == 404 -def test_endpoint_decorator(): +def test_endpoint_decorator(app, client): from werkzeug.routing import Rule - app = flask.Flask(__name__) app.url_map.add(Rule('/foo', endpoint='bar')) bp = flask.Blueprint('bp', __name__) @@ -396,229 +384,282 @@ def test_endpoint_decorator(): app.register_blueprint(bp, url_prefix='/bp_prefix') - c = app.test_client() - assert c.get('/foo').data == b'bar' - assert c.get('/bp_prefix/bar').status_code == 404 + assert client.get('/foo').data == b'bar' + assert client.get('/bp_prefix/bar').status_code == 404 -def test_template_filter(): +def test_template_filter(app): bp = flask.Blueprint('bp', __name__) + @bp.app_template_filter() def my_reverse(s): return s[::-1] - app = flask.Flask(__name__) + app.register_blueprint(bp, url_prefix='/py') assert 'my_reverse' in app.jinja_env.filters.keys() assert app.jinja_env.filters['my_reverse'] == my_reverse assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba' -def test_add_template_filter(): + +def test_add_template_filter(app): bp = flask.Blueprint('bp', __name__) + def my_reverse(s): return s[::-1] + bp.add_app_template_filter(my_reverse) - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') assert 'my_reverse' in app.jinja_env.filters.keys() assert app.jinja_env.filters['my_reverse'] == my_reverse assert app.jinja_env.filters['my_reverse']('abcd') == 'dcba' -def test_template_filter_with_name(): + +def test_template_filter_with_name(app): bp = flask.Blueprint('bp', __name__) + @bp.app_template_filter('strrev') def my_reverse(s): return s[::-1] - app = flask.Flask(__name__) + app.register_blueprint(bp, url_prefix='/py') assert 'strrev' in app.jinja_env.filters.keys() assert app.jinja_env.filters['strrev'] == my_reverse assert app.jinja_env.filters['strrev']('abcd') == 'dcba' -def test_add_template_filter_with_name(): + +def test_add_template_filter_with_name(app): bp = flask.Blueprint('bp', __name__) + def my_reverse(s): return s[::-1] + bp.add_app_template_filter(my_reverse, 'strrev') - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') assert 'strrev' in app.jinja_env.filters.keys() assert app.jinja_env.filters['strrev'] == my_reverse assert app.jinja_env.filters['strrev']('abcd') == 'dcba' -def test_template_filter_with_template(): + +def test_template_filter_with_template(app, client): bp = flask.Blueprint('bp', __name__) + @bp.app_template_filter() def super_reverse(s): return s[::-1] - app = flask.Flask(__name__) + app.register_blueprint(bp, url_prefix='/py') + @app.route('/') def index(): return flask.render_template('template_filter.html', value='abcd') - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.data == b'dcba' -def test_template_filter_after_route_with_template(): - app = flask.Flask(__name__) + +def test_template_filter_after_route_with_template(app, client): @app.route('/') def index(): return flask.render_template('template_filter.html', value='abcd') + bp = flask.Blueprint('bp', __name__) + @bp.app_template_filter() def super_reverse(s): return s[::-1] + app.register_blueprint(bp, url_prefix='/py') - rv = app.test_client().get('/') + rv = client.get('/') assert rv.data == b'dcba' -def test_add_template_filter_with_template(): + +def test_add_template_filter_with_template(app, client): bp = flask.Blueprint('bp', __name__) + def super_reverse(s): return s[::-1] + bp.add_app_template_filter(super_reverse) - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') + @app.route('/') def index(): return flask.render_template('template_filter.html', value='abcd') - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.data == b'dcba' -def test_template_filter_with_name_and_template(): + +def test_template_filter_with_name_and_template(app, client): bp = flask.Blueprint('bp', __name__) + @bp.app_template_filter('super_reverse') def my_reverse(s): return s[::-1] - app = flask.Flask(__name__) + app.register_blueprint(bp, url_prefix='/py') + @app.route('/') def index(): return flask.render_template('template_filter.html', value='abcd') - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.data == b'dcba' -def test_add_template_filter_with_name_and_template(): + +def test_add_template_filter_with_name_and_template(app, client): bp = flask.Blueprint('bp', __name__) + def my_reverse(s): return s[::-1] + bp.add_app_template_filter(my_reverse, 'super_reverse') - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') + @app.route('/') def index(): return flask.render_template('template_filter.html', value='abcd') - rv = app.test_client().get('/') + + rv = client.get('/') assert rv.data == b'dcba' -def test_template_test(): + +def test_template_test(app): bp = flask.Blueprint('bp', __name__) + @bp.app_template_test() def is_boolean(value): return isinstance(value, bool) - app = flask.Flask(__name__) + app.register_blueprint(bp, url_prefix='/py') assert 'is_boolean' in app.jinja_env.tests.keys() assert app.jinja_env.tests['is_boolean'] == is_boolean assert app.jinja_env.tests['is_boolean'](False) -def test_add_template_test(): + +def test_add_template_test(app): bp = flask.Blueprint('bp', __name__) + def is_boolean(value): return isinstance(value, bool) + bp.add_app_template_test(is_boolean) - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') assert 'is_boolean' in app.jinja_env.tests.keys() assert app.jinja_env.tests['is_boolean'] == is_boolean assert app.jinja_env.tests['is_boolean'](False) -def test_template_test_with_name(): + +def test_template_test_with_name(app): bp = flask.Blueprint('bp', __name__) + @bp.app_template_test('boolean') def is_boolean(value): return isinstance(value, bool) - app = flask.Flask(__name__) + app.register_blueprint(bp, url_prefix='/py') assert 'boolean' in app.jinja_env.tests.keys() assert app.jinja_env.tests['boolean'] == is_boolean assert app.jinja_env.tests['boolean'](False) -def test_add_template_test_with_name(): + +def test_add_template_test_with_name(app): bp = flask.Blueprint('bp', __name__) + def is_boolean(value): return isinstance(value, bool) + bp.add_app_template_test(is_boolean, 'boolean') - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') assert 'boolean' in app.jinja_env.tests.keys() assert app.jinja_env.tests['boolean'] == is_boolean assert app.jinja_env.tests['boolean'](False) -def test_template_test_with_template(): + +def test_template_test_with_template(app, client): bp = flask.Blueprint('bp', __name__) + @bp.app_template_test() def boolean(value): return isinstance(value, bool) - app = flask.Flask(__name__) + app.register_blueprint(bp, url_prefix='/py') + @app.route('/') def index(): return flask.render_template('template_test.html', value=False) - rv = app.test_client().get('/') + + rv = client.get('/') assert b'Success!' in rv.data -def test_template_test_after_route_with_template(): - app = flask.Flask(__name__) + +def test_template_test_after_route_with_template(app, client): @app.route('/') def index(): return flask.render_template('template_test.html', value=False) + bp = flask.Blueprint('bp', __name__) + @bp.app_template_test() def boolean(value): return isinstance(value, bool) + app.register_blueprint(bp, url_prefix='/py') - rv = app.test_client().get('/') + rv = client.get('/') assert b'Success!' in rv.data -def test_add_template_test_with_template(): + +def test_add_template_test_with_template(app, client): bp = flask.Blueprint('bp', __name__) + def boolean(value): return isinstance(value, bool) + bp.add_app_template_test(boolean) - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') + @app.route('/') def index(): return flask.render_template('template_test.html', value=False) - rv = app.test_client().get('/') + + rv = client.get('/') assert b'Success!' in rv.data -def test_template_test_with_name_and_template(): + +def test_template_test_with_name_and_template(app, client): bp = flask.Blueprint('bp', __name__) + @bp.app_template_test('boolean') def is_boolean(value): return isinstance(value, bool) - app = flask.Flask(__name__) + app.register_blueprint(bp, url_prefix='/py') + @app.route('/') def index(): return flask.render_template('template_test.html', value=False) - rv = app.test_client().get('/') + + rv = client.get('/') assert b'Success!' in rv.data -def test_add_template_test_with_name_and_template(): + +def test_add_template_test_with_name_and_template(app, client): bp = flask.Blueprint('bp', __name__) + def is_boolean(value): return isinstance(value, bool) + bp.add_app_template_test(is_boolean, 'boolean') - app = flask.Flask(__name__) app.register_blueprint(bp, url_prefix='/py') + @app.route('/') def index(): return flask.render_template('template_test.html', value=False) - rv = app.test_client().get('/') + + rv = client.get('/') assert b'Success!' in rv.data + def test_context_processing(): app = flask.Flask(__name__) answer_bp = flask.Blueprint('answer_bp', __name__) @@ -661,12 +702,15 @@ def test_context_processing(): assert b'42' in answer_page_bytes assert b'43' in answer_page_bytes + def test_template_global(): app = flask.Flask(__name__) bp = flask.Blueprint('bp', __name__) + @bp.app_template_global() def get_answer(): return 42 + # Make sure the function is not in the jinja_env already assert 'get_answer' not in app.jinja_env.globals.keys() app.register_blueprint(bp) diff --git a/tests/test_deprecations.py b/tests/test_deprecations.py index 666f7d56..7383604e 100644 --- a/tests/test_deprecations.py +++ b/tests/test_deprecations.py @@ -15,10 +15,8 @@ import flask class TestRequestDeprecation(object): - - def test_request_json(self, recwarn): + def test_request_json(self, recwarn, app, client): """Request.json is deprecated""" - app = flask.Flask(__name__) app.testing = True @app.route('/', methods=['POST']) @@ -27,13 +25,11 @@ class TestRequestDeprecation(object): print(flask.request.json) return 'OK' - c = app.test_client() - c.post('/', data='{"spam": 42}', content_type='application/json') + client.post('/', data='{"spam": 42}', content_type='application/json') recwarn.pop(DeprecationWarning) - def test_request_module(self, recwarn): + def test_request_module(self, recwarn, app, client): """Request.module is deprecated""" - app = flask.Flask(__name__) app.testing = True @app.route('/') @@ -41,6 +37,5 @@ class TestRequestDeprecation(object): assert flask.request.module is None return 'OK' - c = app.test_client() - c.get('/') + client.get('/') recwarn.pop(DeprecationWarning) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 5af95f18..4259d2d9 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -35,240 +35,226 @@ def has_encoding(name): class TestJSON(object): - - def test_ignore_cached_json(self): - app = flask.Flask(__name__) + def test_ignore_cached_json(self, app): with app.test_request_context('/', method='POST', data='malformed', content_type='application/json'): assert flask.request.get_json(silent=True, cache=True) is None with pytest.raises(BadRequest): flask.request.get_json(silent=False, cache=False) - def test_post_empty_json_adds_exception_to_response_content_in_debug(self): - app = flask.Flask(__name__) + def test_post_empty_json_adds_exception_to_response_content_in_debug(self, app, client): app.config['DEBUG'] = True + @app.route('/json', methods=['POST']) def post_json(): flask.request.get_json() return None - c = app.test_client() - rv = c.post('/json', data=None, content_type='application/json') + + rv = client.post('/json', data=None, content_type='application/json') assert rv.status_code == 400 assert b'Failed to decode JSON object' in rv.data - def test_post_empty_json_wont_add_exception_to_response_if_no_debug(self): - app = flask.Flask(__name__) + def test_post_empty_json_wont_add_exception_to_response_if_no_debug(self, app, client): app.config['DEBUG'] = False + @app.route('/json', methods=['POST']) def post_json(): flask.request.get_json() return None - c = app.test_client() - rv = c.post('/json', data=None, content_type='application/json') + + rv = client.post('/json', data=None, content_type='application/json') assert rv.status_code == 400 assert b'Failed to decode JSON object' not in rv.data - def test_json_bad_requests(self): - app = flask.Flask(__name__) + def test_json_bad_requests(self, app, client): + @app.route('/json', methods=['POST']) def return_json(): return flask.jsonify(foo=text_type(flask.request.get_json())) - c = app.test_client() - rv = c.post('/json', data='malformed', content_type='application/json') + + rv = client.post('/json', data='malformed', content_type='application/json') assert rv.status_code == 400 - def test_json_custom_mimetypes(self): - app = flask.Flask(__name__) + def test_json_custom_mimetypes(self, app, client): + @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') + + rv = client.post('/json', data='"foo"', content_type='application/x+json') assert rv.data == b'foo' - def test_json_body_encoding(self): - app = flask.Flask(__name__) + def test_json_body_encoding(self, app, client): app.testing = True + @app.route('/') def index(): return flask.request.get_json() - c = app.test_client() - resp = c.get('/', data=u'"Hällo Wörld"'.encode('iso-8859-15'), - content_type='application/json; charset=iso-8859-15') + resp = client.get('/', data=u'"Hällo Wörld"'.encode('iso-8859-15'), + content_type='application/json; charset=iso-8859-15') assert resp.data == u'Hällo Wörld'.encode('utf-8') - def test_json_as_unicode(self): - app = flask.Flask(__name__) + @pytest.mark.parametrize('test_value,expected', [(True, '"\\u2603"'), (False, u'"\u2603"')]) + def test_json_as_unicode(self, test_value, expected, app, app_ctx): - app.config['JSON_AS_ASCII'] = True - with app.app_context(): - rv = flask.json.dumps(u'\N{SNOWMAN}') - assert rv == '"\\u2603"' + app.config['JSON_AS_ASCII'] = test_value + rv = flask.json.dumps(u'\N{SNOWMAN}') + assert rv == expected - app.config['JSON_AS_ASCII'] = False - with app.app_context(): - rv = flask.json.dumps(u'\N{SNOWMAN}') - assert rv == u'"\u2603"' - - def test_json_dump_to_file(self): - app = flask.Flask(__name__) + def test_json_dump_to_file(self, app, app_ctx): test_data = {'name': 'Flask'} out = StringIO() - with app.app_context(): - flask.json.dump(test_data, out) - out.seek(0) - rv = flask.json.load(out) - assert rv == test_data + flask.json.dump(test_data, out) + out.seek(0) + rv = flask.json.load(out) + assert rv == test_data @pytest.mark.parametrize('test_value', [0, -1, 1, 23, 3.14, 's', "longer string", True, False, None]) - def test_jsonify_basic_types(self, test_value): + def test_jsonify_basic_types(self, test_value, app, client): """Test jsonify with basic types.""" - app = flask.Flask(__name__) - c = app.test_client() url = '/jsonify_basic_types' app.add_url_rule(url, url, lambda x=test_value: flask.jsonify(x)) - rv = c.get(url) + rv = client.get(url) assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data) == test_value - def test_jsonify_dicts(self): + def test_jsonify_dicts(self, app, client): """Test jsonify with dicts and kwargs unpacking.""" - d = dict( - a=0, b=23, c=3.14, d='t', e='Hi', f=True, g=False, - h=['test list', 10, False], - i={'test':'dict'} - ) - app = flask.Flask(__name__) + d = {'a': 0, 'b': 23, 'c': 3.14, 'd': 't', + 'e': 'Hi', 'f': True, 'g': False, + 'h': ['test list', 10, False], + 'i': {'test': 'dict'}} + @app.route('/kw') def return_kwargs(): return flask.jsonify(**d) + @app.route('/dict') def return_dict(): return flask.jsonify(d) - c = app.test_client() + for url in '/kw', '/dict': - rv = c.get(url) + rv = client.get(url) assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data) == d - def test_jsonify_arrays(self): + def test_jsonify_arrays(self, app, client): """Test jsonify of lists and args unpacking.""" l = [ 0, 42, 3.14, 't', 'hello', True, False, ['test list', 2, False], - {'test':'dict'} + {'test': 'dict'} ] - app = flask.Flask(__name__) + @app.route('/args_unpack') def return_args_unpack(): return flask.jsonify(*l) + @app.route('/array') def return_array(): return flask.jsonify(l) - c = app.test_client() + for url in '/args_unpack', '/array': - rv = c.get(url) + rv = client.get(url) assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data) == l - def test_jsonify_date_types(self): + def test_jsonify_date_types(self, app, client): """Test jsonify with datetime.date and datetime.datetime types.""" test_dates = ( datetime.datetime(1973, 3, 11, 6, 30, 45), datetime.date(1975, 1, 5) ) - app = flask.Flask(__name__) - c = app.test_client() for i, d in enumerate(test_dates): url = '/datetest{0}'.format(i) app.add_url_rule(url, str(i), lambda val=d: flask.jsonify(x=val)) - rv = c.get(url) + rv = client.get(url) assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple()) - def test_jsonify_uuid_types(self): + def test_jsonify_uuid_types(self, app, client): """Test jsonify with uuid.UUID types""" test_uuid = uuid.UUID(bytes=b'\xDE\xAD\xBE\xEF' * 4) - app = flask.Flask(__name__) url = '/uuid_test' app.add_url_rule(url, url, lambda: flask.jsonify(x=test_uuid)) - c = app.test_client() - rv = c.get(url) + rv = client.get(url) rv_x = flask.json.loads(rv.data)['x'] assert rv_x == str(test_uuid) rv_uuid = uuid.UUID(rv_x) assert rv_uuid == test_uuid - def test_json_attr(self): - app = flask.Flask(__name__) + def test_json_attr(self, app, client): + @app.route('/add', methods=['POST']) def add(): json = flask.request.get_json() return text_type(json['a'] + json['b']) - c = app.test_client() - rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}), - content_type='application/json') + + rv = client.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}), + content_type='application/json') assert rv.data == b'3' - def test_template_escaping(self): - app = flask.Flask(__name__) + def test_template_escaping(self, app, req_ctx): render = flask.render_template_string - with app.test_request_context(): - rv = flask.json.htmlsafe_dumps('') - assert rv == u'"\\u003c/script\\u003e"' - assert type(rv) == text_type - rv = render('{{ ""|tojson }}') - assert rv == '"\\u003c/script\\u003e"' - rv = render('{{ "<\0/script>"|tojson }}') - assert rv == '"\\u003c\\u0000/script\\u003e"' - rv = render('{{ " diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 86aa7578..f6ff7015 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -1,46 +1,75 @@ -========================== How to contribute to Flask ========================== -Thanks for considering contributing to Flask. +Thank you for considering contributing to Flask! Support questions -================= +----------------- + +Please, don't use the issue tracker for this. Use one of the following +resources for questions about your own code: + +* The IRC channel ``#pocoo`` on FreeNode. +* The IRC channel ``#python`` on FreeNode for more general questions. +* The mailing list flask@python.org for long term discussion or larger issues. +* Ask on `Stack Overflow`_. Search with Google first using: + ``site:stackoverflow.com flask {search term, exception message, etc.}`` -Please, don't use the issue tracker for this. Check whether the ``#pocoo`` IRC -channel on Freenode can help with your issue. If your problem is not strictly -Werkzeug or Flask specific, ``#python`` is generally more active. -`Stack Overflow `_ is also worth considering. +.. _Stack Overflow: https://stackoverflow.com/questions/tagged/flask?sort=linked Reporting issues -================ +---------------- -- Under which versions of Python does this happen? This is even more important - if your issue is encoding related. +- Describe what you expected to happen. +- If possible, include a `minimal, complete, and verifiable example`_ to help + us identify the issue. This also helps check that the issue is not with your + own code. +- Describe what actually happened. Include the full traceback if there was an + exception. +- List your Python, Flask, and Werkzeug versions. If possible, check if this + issue is already fixed in the repository. -- Under which versions of Werkzeug does this happen? Check if this issue is - fixed in the repository. +.. _minimal, complete, and verifiable example: https://stackoverflow.com/help/mcve Submitting patches -================== +------------------ - Include tests if your patch is supposed to solve a bug, and explain clearly under which circumstances the bug happens. Make sure the test fails without your patch. - -- Try to follow `PEP8 `_, but you - may ignore the line-length-limit if following it would make the code uglier. +- Try to follow `PEP8`_, but you may ignore the line length limit if following + it would make the code uglier. First time setup ----------------- +~~~~~~~~~~~~~~~~ - Download and install the `latest version of git`_. -- Configure git with your `username`_ and `email`_. +- Configure git with your `username`_ and `email`_:: + + git config --global user.name 'your name' + git config --global user.email 'your email' + - Make sure you have a `GitHub account`_. - Fork Flask to your GitHub account by clicking the `Fork`_ button. -- `Clone`_ your GitHub fork locally. -- Add the main repository as a remote to update later. - ``git remote add pallets https://github.com/pallets/flask`` +- `Clone`_ your GitHub fork locally:: + + git clone https://github.com/{username}/flask + cd flask + +- Add the main repository as a remote to update later:: + + git remote add pallets https://github.com/pallets/flask + git fetch pallets + +- Create a virtualenv:: + + python3 -m venv env + . env/bin/activate + # or "env\Scripts\activate" on Windows + +- Install Flask in editable mode with development dependencies:: + + pip install -e ".[dev]" .. _GitHub account: https://github.com/join .. _latest version of git: https://git-scm.com/downloads @@ -50,7 +79,7 @@ First time setup .. _Clone: https://help.github.com/articles/fork-a-repo/#step-2-create-a-local-clone-of-your-fork Start coding ------------- +~~~~~~~~~~~~ - Create a branch to identify the issue you would like to work on (e.g. ``2287-dry-test-suite``) @@ -68,98 +97,70 @@ Start coding .. _contributing-testsuite: -Running the testsuite ---------------------- +Running the tests +~~~~~~~~~~~~~~~~~ -You probably want to set up a `virtualenv -`_. +Run the basic test suite with:: -The minimal requirement for running the testsuite is ``pytest``. You can -install it with:: + pytest - pip install pytest +This only runs the tests for the current environment. Whether this is relevant +depends on which part of Flask you're working on. Travis-CI will run the full +suite when you submit your pull request. -Clone this repository:: +The full test suite takes a long time to run because it tests multiple +combinations of Python and dependencies. You need to have Python 2.6, 2.7, 3.3, +3.4, 3.5 3.6, and PyPy 2.7 installed to run all of the environments. Then run:: - git clone https://github.com/pallets/flask.git - -Install Flask as an editable package using the current source:: - - cd flask - pip install --editable . - -Running the testsuite ---------------------- - -The minimal requirement for running the testsuite is ``pytest``. You can -install it with:: - - pip install pytest - -Then you can run the testsuite with:: - - pytest tests/ - -**Shortcut**: ``make test`` will ensure ``pytest`` is installed, and run it. - -With only pytest installed, a large part of the testsuite will get skipped -though. Whether this is relevant depends on which part of Flask you're working -on. Travis is set up to run the full testsuite when you submit your pull -request anyways. - -If you really want to test everything, you will have to install ``tox`` instead -of ``pytest``. You can install it with:: - - pip install tox - -The ``tox`` command will then run all tests against multiple combinations -Python versions and dependency versions. - -**Shortcut**: ``make tox-test`` will ensure ``tox`` is installed, and run it. + tox Running test coverage ---------------------- -Generating a report of lines that do not have unit test coverage can indicate where -to start contributing. ``pytest`` integrates with ``coverage.py``, using the ``pytest-cov`` -plugin. This assumes you have already run the testsuite (see previous section):: +~~~~~~~~~~~~~~~~~~~~~ - pip install pytest-cov +Generating a report of lines that do not have test coverage can indicate +where to start contributing. Run ``pytest`` using ``coverage`` and generate a +report on the terminal and as an interactive HTML document:: -After this has been installed, you can output a report to the command line using this command:: + coverage run -m pytest + coverage report + coverage html + # then open htmlcov/index.html - pytest --cov=flask tests/ +Read more about `coverage `_. -Generate a HTML report can be done using this command:: +Running the full test suite with ``tox`` will combine the coverage reports +from all runs. - pytest --cov-report html --cov=flask tests/ +``make`` targets +~~~~~~~~~~~~~~~~ -Full docs on ``coverage.py`` are here: https://coverage.readthedocs.io +Flask provides a ``Makefile`` with various shortcuts. They will ensure that +all dependencies are installed. -**Shortcut**: ``make cov`` will ensure ``pytest-cov`` is installed, run it, display the results, *and* save the HTML report. +- ``make test`` runs the basic test suite with ``pytest`` +- ``make cov`` runs the basic test suite with ``coverage`` +- ``make test-all`` runs the full test suite with ``tox`` +- ``make docs`` builds the HTML documentation +Caution: zero-padded file modes +------------------------------- -Caution -======= -pushing -------- -This repository contains several zero-padded file modes that may cause issues when pushing this repository to git hosts other than github. Fixing this is destructive to the commit history, so we suggest ignoring these warnings. If it fails to push and you're using a self-hosted git service like Gitlab, you can turn off repository checks in the admin panel. +This repository contains several zero-padded file modes that may cause issues +when pushing this repository to git hosts other than GitHub. Fixing this is +destructive to the commit history, so we suggest ignoring these warnings. If it +fails to push and you're using a self-hosted git service like GitLab, you can +turn off repository checks in the admin panel. - -cloning -------- -The zero-padded file modes files above can cause issues while cloning, too. If you have - -:: +These files can also cause issues while cloning. If you have :: [fetch] fsckobjects = true -or - -:: +or :: [receive] fsckObjects = true - -set in your git configuration file, cloning this repository will fail. The only solution is to set both of the above settings to false while cloning, and then setting them back to true after the cloning is finished. +set in your git configuration file, cloning this repository will fail. The only +solution is to set both of the above settings to false while cloning, and then +setting them back to true after the cloning is finished. diff --git a/Makefile b/Makefile index 9a0a1696..aef8a782 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,28 @@ -.PHONY: clean-pyc ext-test test tox-test test-with-mem upload-docs docs audit +.PHONY: all install-dev test coverage cov test-all tox docs audit release clean-pyc upload-docs ebook -all: clean-pyc test +all: test -test: - pip install -r test-requirements.txt - tox -e py-release +install-dev: + pip install -q -e .[dev] -cov: - pip install -r test-requirements.txt -q - FLASK_DEBUG= py.test --cov-report term --cov-report html --cov=flask --cov=examples tests examples +test: clean-pyc install-dev + pytest + +coverage: clean-pyc install-dev + pip install -q -e .[test] + coverage run -m pytest + coverage report + coverage html + +cov: coverage + +test-all: install-dev + tox + +tox: test-all + +docs: clean-pyc install-dev + $(MAKE) -C docs html audit: python setup.py audit @@ -16,9 +30,6 @@ audit: release: python scripts/make-release.py -ext-test: - python tests/flaskext_test.py --browse - clean-pyc: find . -name '*.pyc' -exec rm -f {} + find . -name '*.pyo' -exec rm -f {} + @@ -40,6 +51,3 @@ ebook: @echo 'Requires X-forwarding for Qt features used in conversion (ssh -X).' @echo 'Do not mind "Invalid value for ..." CSS errors if .mobi renders.' ssh -X pocoo.org ebook-convert /var/www/flask.pocoo.org/docs/flask-docs.epub /var/www/flask.pocoo.org/docs/flask-docs.mobi --cover http://flask.pocoo.org/docs/_images/logo-full.png --authors 'Armin Ronacher' - -docs: - $(MAKE) -C docs html diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc index 8b25e61d..e77f7b60 100644 --- a/docs/contents.rst.inc +++ b/docs/contents.rst.inc @@ -59,3 +59,4 @@ Design notes, legal information and changelog are here for the interested. upgrading changelog license + contributing diff --git a/docs/contributing.rst b/docs/contributing.rst new file mode 100644 index 00000000..e582053e --- /dev/null +++ b/docs/contributing.rst @@ -0,0 +1 @@ +.. include:: ../CONTRIBUTING.rst diff --git a/setup.py b/setup.py index d8cd874a..0abf22a7 100644 --- a/setup.py +++ b/setup.py @@ -48,14 +48,12 @@ import re import ast from setuptools import setup - _version_re = re.compile(r'__version__\s+=\s+(.*)') with open('flask/__init__.py', 'rb') as f: version = str(ast.literal_eval(_version_re.search( f.read().decode('utf-8')).group(1))) - setup( name='Flask', version=version, @@ -76,6 +74,17 @@ setup( 'itsdangerous>=0.21', 'click>=4.0', ], + extras_require={ + 'dev': [ + 'blinker', + 'greenlet', + 'pytest>=3', + 'coverage', + 'tox', + 'sphinx', + 'sphinxcontrib-log-cabinet' + ], + }, classifiers=[ 'Development Status :: 4 - Beta', 'Environment :: Web Environment', diff --git a/tox.ini b/tox.ini index 1a9f4d9d..cb6dd342 100644 --- a/tox.ini +++ b/tox.ini @@ -1,7 +1,7 @@ [tox] envlist = - py{36,35,34,33,27,26,py}-release - py{36,27,py}-release-simplejson + py{36,35,34,33,27,26,py} + py{36,27,py}-simplejson py{36,33,27,26,py}-devel py{36,33,27,26,py}-lowest docs-html @@ -35,12 +35,10 @@ commands = pip install -e examples/patterns/largerapp -q # pytest-cov doesn't seem to play nice with -p - coverage run -p -m pytest + coverage run -p -m pytest tests examples [testenv:docs-html] -deps = - sphinx - flask-sphinx-themes +deps = sphinx commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html [testenv:docs-linkcheck] @@ -63,3 +61,10 @@ commands = coverage combine coverage report codecov + +[testenv:detox] +skip_install = true +deps = detox +commands = + detox -e py{36,35,34,33,27,26,py},py{36,27,py}-simplejson,py{36,33,27,26,py}-devel,py{36,33,27,26,py}-lowest,docs-html + tox -e coverage-report From 399ac3c54fff4cb9d5c4673ce98e409a562b5745 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 28 May 2017 11:52:01 -0700 Subject: [PATCH 254/399] update changelog [ci skip] --- CHANGES | 38 ++++++++++++++++++++++++++++---------- setup.py | 1 + 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index edbd652e..e41028c8 100644 --- a/CHANGES +++ b/CHANGES @@ -8,16 +8,19 @@ Version 0.13 Major release, unreleased -- Make `app.run()` into a noop if a Flask application is run from the - development server on the command line. This avoids some behavior that +- Minimum Werkzeug version bumped to 0.9, but please use the latest version. +- Minimum Click version bumped to 4, but please use the latest version. +- Make ``app.run()`` into a noop if a Flask application is run from the + development server on the command line. This avoids some behavior that was confusing to debug for newcomers. -- Change default configuration `JSONIFY_PRETTYPRINT_REGULAR=False`. jsonify() - method returns compressed response by default, and pretty response in - debug mode. -- Change Flask.__init__ to accept two new keyword arguments, ``host_matching`` - and ``static_host``. This enables ``host_matching`` to be set properly by the - time the constructor adds the static route, and enables the static route to - be properly associated with the required host. (``#1559``) +- Change default configuration ``JSONIFY_PRETTYPRINT_REGULAR=False``. + ``jsonify()`` method returns compressed response by default, and pretty + response in debug mode. (`#2193`_) +- Change ``Flask.__init__`` to accept two new keyword arguments, + ``host_matching`` and ``static_host``. This enables ``host_matching`` to be + set properly by the time the constructor adds the static route, and enables + the static route to be properly associated with the required host. + (``#1559``) - ``send_file`` supports Unicode in ``attachment_filename``. (`#2223`_) - Pass ``_scheme`` argument from ``url_for`` to ``handle_build_error``. (`#2017`_) @@ -46,19 +49,34 @@ Major release, unreleased work with the ``flask`` command. If they take a single parameter or a parameter named ``script_info``, the ``ScriptInfo`` object will be passed. (`#2319`_) -- FLASK_APP=myproject.app:create_app('dev') support. +- ``FLASK_APP`` can be set to an app factory, with arguments if needed, for + example ``FLASK_APP=myproject.app:create_app('dev')``. (`#2326`_) +- ``View.provide_automatic_options = True`` is set on the view function from + ``View.as_view``, to be detected in ``app.add_url_rule``. (`#2316`_) +- Error handling will try handlers registered for ``blueprint, code``, + ``app, code``, ``blueprint, exception``, ``app, exception``. (`#2314`_) +- ``Cookie`` is added to the response's ``Vary`` header if the session is + accessed at all during the request (and it wasn't deleted). (`#2288`_) +- ``app.test_request_context()`` take ``subdomain`` and ``url_scheme`` + parameters for use when building base URL. (`#1621`_) .. _#1489: https://github.com/pallets/flask/pull/1489 +.. _#1621: https://github.com/pallets/flask/pull/1621 .. _#1898: https://github.com/pallets/flask/pull/1898 .. _#1936: https://github.com/pallets/flask/pull/1936 .. _#2017: https://github.com/pallets/flask/pull/2017 +.. _#2193: https://github.com/pallets/flask/pull/2193 .. _#2223: https://github.com/pallets/flask/pull/2223 .. _#2254: https://github.com/pallets/flask/pull/2254 .. _#2256: https://github.com/pallets/flask/pull/2256 .. _#2259: https://github.com/pallets/flask/pull/2259 .. _#2282: https://github.com/pallets/flask/pull/2282 +.. _#2288: https://github.com/pallets/flask/pull/2288 .. _#2297: https://github.com/pallets/flask/pull/2297 +.. _#2314: https://github.com/pallets/flask/pull/2314 +.. _#2316: https://github.com/pallets/flask/pull/2316 .. _#2319: https://github.com/pallets/flask/pull/2319 +.. _#2326: https://github.com/pallets/flask/pull/2326 Version 0.12.2 -------------- diff --git a/setup.py b/setup.py index 0abf22a7..24cd9d83 100644 --- a/setup.py +++ b/setup.py @@ -99,6 +99,7 @@ setup( 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ], From 60feecc26c60b8b6ba5349baf8d4cca5cbfed223 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 28 May 2017 14:04:18 -0700 Subject: [PATCH 255/399] reformat config from table to linkable sections --- docs/config.rst | 411 ++++++++++++++++++++++++++++-------------------- 1 file changed, 240 insertions(+), 171 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 714b54c8..9fffa09f 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -3,8 +3,6 @@ Configuration Handling ====================== -.. versionadded:: 0.3 - 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 @@ -54,182 +52,252 @@ method:: $ flask run (On Windows you need to use ``set`` instead of ``export``). - - ``app.debug`` and ``app.config['DEBUG']`` are not compatible with + + ``app.debug`` and ``app.config['DEBUG']`` are not compatible with   the :command:`flask` script. They only worked when using ``Flask.run()`` method. - + Builtin Configuration Values ---------------------------- The following configuration values are used internally by Flask: -.. tabularcolumns:: |p{6.5cm}|p{8.5cm}| - -================================= ========================================= -``DEBUG`` enable/disable debug mode when using - ``Flask.run()`` method to start server -``TESTING`` enable/disable testing mode -``PROPAGATE_EXCEPTIONS`` explicitly enable or disable the - propagation of exceptions. If not set or - explicitly set to ``None`` this is - implicitly true if either ``TESTING`` or - ``DEBUG`` is true. -``PRESERVE_CONTEXT_ON_EXCEPTION`` By default if the application is in - debug mode the request context is not - popped on exceptions to enable debuggers - to introspect the data. This can be - disabled by this key. You can also use - this setting to force-enable it for non - debug execution which might be useful to - debug production applications (but also - very risky). -``SECRET_KEY`` the secret key -``SESSION_COOKIE_NAME`` the name of the session cookie -``SESSION_COOKIE_DOMAIN`` the domain for the session cookie. If - this is not set, the cookie will be - valid for all subdomains of - ``SERVER_NAME``. -``SESSION_COOKIE_PATH`` the path for the session cookie. If - this is not set the cookie will be valid - for all of ``APPLICATION_ROOT`` or if - that is not set for ``'/'``. -``SESSION_COOKIE_HTTPONLY`` controls if the cookie should be set - with the httponly flag. Defaults to - ``True``. -``SESSION_COOKIE_SECURE`` controls if the cookie should be set - with the secure flag. Defaults to - ``False``. -``PERMANENT_SESSION_LIFETIME`` the lifetime of a permanent session as - :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 refreshed. 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 -``LOGGER_HANDLER_POLICY`` the policy of the default logging - handler. The default is ``'always'`` - which means that the default logging - handler is always active. ``'debug'`` - will only activate logging in debug - mode, ``'production'`` will only log in - production and ``'never'`` disables it - entirely. -``SERVER_NAME`` the name and port number of the server. - Required for subdomain support (e.g.: - ``'myapp.dev:5000'``) Note that - localhost does not support subdomains so - setting this to “localhost” does not - help. Setting a ``SERVER_NAME`` also - by default enables URL generation - without a request context but with an - application context. -``APPLICATION_ROOT`` The path value used for the session - cookie if ``SESSION_COOKIE_PATH`` isn't - set. If it's also ``None`` ``'/'`` is used. - Note that to actually serve your Flask - app under a subpath you need to tell - your WSGI container the ``SCRIPT_NAME`` - WSGI environment variable. -``MAX_CONTENT_LENGTH`` If set to a value in bytes, Flask will - reject incoming requests with a - content length greater than this by - returning a 413 status code. -``SEND_FILE_MAX_AGE_DEFAULT`` Default cache control max age to use with - :meth:`~flask.Flask.send_static_file` (the - default static file handler) and - :func:`~flask.send_file`, as - :class:`datetime.timedelta` or as seconds. - Override this value on a per-file - basis using the - :meth:`~flask.Flask.get_send_file_max_age` - hook on :class:`~flask.Flask` or - :class:`~flask.Blueprint`, - respectively. Defaults to 43200 (12 hours). -``TRAP_HTTP_EXCEPTIONS`` If this is set to ``True`` Flask will - not execute the error handlers of HTTP - exceptions but instead treat the - exception like any other and bubble it - through the exception stack. This is - helpful for hairy debugging situations - where you have to find out where an HTTP - exception is coming from. -``TRAP_BAD_REQUEST_ERRORS`` Werkzeug's internal data structures that - deal with request specific data will - raise special key errors that are also - bad request exceptions. Likewise many - operations can implicitly fail with a - BadRequest exception for consistency. - Since it's nice for debugging to know - why exactly it failed this flag can be - used to debug those situations. If this - config is set to ``True`` you will get - a regular traceback instead. -``PREFERRED_URL_SCHEME`` The URL scheme that should be used for - URL generation if no URL scheme is - available. This defaults to ``http``. -``JSON_AS_ASCII`` By default Flask serialize object to - ascii-encoded JSON. If this is set to - ``False`` Flask will not encode to ASCII - and output strings as-is and return - unicode strings. ``jsonify`` will - automatically encode it in ``utf-8`` - then for transport for instance. -``JSON_SORT_KEYS`` By default Flask will serialize JSON - objects in a way that the keys are - ordered. This is done in order to - ensure that independent of the hash seed - of the dictionary the return value will - be consistent to not trash external HTTP - caches. You can override the default - behavior by changing this variable. - This is not recommended but might give - you a performance improvement on the - cost of cacheability. -``JSONIFY_PRETTYPRINT_REGULAR`` If this is set to ``True`` or the Flask app - is running in debug mode, jsonify responses - will be pretty printed. -``JSONIFY_MIMETYPE`` MIME type used for jsonify responses. -``TEMPLATES_AUTO_RELOAD`` Whether to check for modifications of - the template source and reload it - automatically. By default the value is - ``None`` which means that Flask checks - original file only in debug mode. -``EXPLAIN_TEMPLATE_LOADING`` If this is enabled then every attempt to - load a template will write an info - message to the logger explaining the - attempts to locate the template. This - can be useful to figure out why - templates cannot be found or wrong - templates appear to be loaded. -================================= ========================================= - -.. admonition:: More on ``SERVER_NAME`` - - The ``SERVER_NAME`` key is used for the subdomain support. Because - Flask cannot guess the subdomain part without the knowledge of the - actual server name, this is required if you want to work with - subdomains. This is also used for the session cookie. - - Please keep in mind that not only Flask has the problem of not knowing - what subdomains are, your web browser does as well. Most modern web - 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 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`_. - -.. _bind: https://www.isc.org/downloads/bind/ +.. py:data:: DEBUG + + Enable debug mode. When using the development server with ``flask run`` or + ``app.run``, an interactive debugger will be shown for unhanlded + exceptions, and the server will be reloaded when code changes. + + **Do not enable debug mode in production.** + + Default: ``False`` + +.. py:data:: TESTING + + Enable testing mode. Exceptions are propagated rather than handled by the + the app's error handlers. Extensions may also change their behavior to + facilitate easier testing. You should enable this in your own tests. + + Default: ``False`` + +.. py:data:: PROPAGATE_EXCEPTIONS + + Exceptions are re-raised rather than being handled by the app's error + handlers. If not set, this is implicitly true if ``TESTING`` or ``DEBUG`` + is enabled. + + Default: ``None`` + +.. py:data:: PRESERVE_CONTEXT_ON_EXCEPTION + + Don't pop the request context when an exception occurs. If not set, this + is true if ``DEBUG`` is true. This allows debuggers to introspect the + request data on errors, and should normally not need to be set directly. + + Default: ``None`` + +.. py:data:: TRAP_HTTP_EXCEPTIONS + + If there is no handler for an ``HTTPException``-type exception, re-raise it + to be handled by the interactive debugger instead of returning it as a + simple error response. + + Default: ``False`` + +.. py:data:: TRAP_BAD_REQUEST_ERRORS`` + + Trying to access a key that doesn't exist from request dicts like ``args`` + and ``form`` will return a 400 Bad Request error page. Enable this to treat + the error as an unhandled exception instead so that you get the interactive + debugger. This is a more specific version of ``TRAP_HTTP_EXCEPTIONS``. + + Default: ``False`` + +.. py:data:: SECRET_KEY + + A secret key that will be used for securely signing the session cookie + and can be used for any other security related needs by extensions or your + application. It should be a long random string of bytes, although unicode + is accepted too. For example, copy the output of this to your config:: + + python -c 'import os; print(os.urandom(32))' + + **Do not reveal the secret key when posting questions or committing code.** + + Default: ``None`` + +.. py:data:: SESSION_COOKIE_NAME + + The name of the session cookie. Can be changed in case you already have a + cookie with the same name. + + Default: ``'session'`` + +.. py:data:: SESSION_COOKIE_DOMAIN + + The domain match rule that the session cookie will be valid for. If not + set, the cookie will be valid for all subdomains of ``SERVER_NAME``. If + ``False``, the cookie's domain will not be set. + + Default: ``None`` + +.. py:data:: SESSION_COOKIE_PATH + + The path that the session cookie will be valid for. If not set, the cookie + will be valid underneath ``APPLICATION_ROOT`` or ``/`` if that is not set. + + Default: ``None`` + +.. py:data:: SESSION_COOKIE_HTTPONLY + + Browsers will not allow JavaScript access to cookies marked as "HTTP only" + for security. + + Default: ``True`` + +.. py:data:: SESSION_COOKIE_SECURE + + Browsers will only send cookies with requests over HTTPS if the cookie is + marked "secure". The application must be served over HTTPS for this to make + sense. + + Default: ``False`` + +.. py:data:: SESSION_COOKIE_LIFETIME + + If ``session.permanent`` is true, the cookie's max age will be set to this + number of seconds. Can either be a :class:`datetime.timedelta` or an + ``int``. + + Default: ``timedelta(days=31)`` (``2678400`` seconds) + +.. py:data:: SESSION_REFRESH_EACH_REQUEST + + Control whether the cookie is sent with every response when + ``session.permanent`` is true. Sending the cookie every time (the default) + can more reliably keep the session from expiring, but uses more bandwidth. + Non-permanent sessions are not affected. + + Default: ``True`` + +.. py:data:: USE_X_SENDFILE + + When serving files, set the ``X-Sendfile`` header instead of serving the + data with Flask. Some web servers, such as Apache, recognize this and serve + the data more efficiently. This only makes sense when using such a server. + + Default: ``False`` + +.. py:data:: SEND_FILE_MAX_AGE + + When serving files, set the cache control max age to this number of + seconds. Can either be a :class:`datetime.timedelta` or an ``int``. + Override this value on a per-file basis using + :meth:`~flask.Flask.get_send_file_max_age` on the application or blueprint. + + Default: ``timedelta(hours=12)`` (``43200`` seconds) + +.. py:data:: LOGGER_NAME + + The name of the logger that the Flask application sets up. If not set, + it will take the import name passed to ``Flask.__init__``. + + Default: ``None`` + +.. py:data:: LOGGER_HANDLER_POLICY + + When to activate the application's logger handler. ``'always'`` always + enables it, ``'debug'`` only activates it in debug mode, ``'production'`` + only activates it when not in debug mode, and ``'never'`` never enables it. + + Default: ``'always'`` + +.. py:data:: SERVER_NAME + + Inform the application what host and port it is bound to. Required for + subdomain route matching support. + + If set, will be used for the session cookie domain if + ``SESSION_COOKIE_DOMAIN`` is not set. Modern web browsers will not allow + setting cookies for domains without a dot. To use a domain locally, + add any names that should route to the app to your ``hosts`` file. :: + + 127.0.0.1 localhost.dev + + If set, ``url_for`` can generate external URLs with only an application + context instead of a request context. + + Default: ``None`` + +.. py:data:: APPLICATION_ROOT + + Inform the application what path it is mounted under by the application / + web server. + + Will be used for the session cookie path if ``SESSION_COOKIE_PATH`` is not + set. + + Default: ``'/'`` + +.. py:data:: PREFERRED_URL_SCHEME + + Use this scheme for generating external URLs when not in a request context. + + Default: ``'http'`` + +.. py:data:: MAX_CONTENT_LENGTH + + Don't read more than this many bytes from the incoming request data. If not + set and the request does not specify a ``CONTENT_LENGTH``, no data will be + read for security. + + Default: ``None`` + +.. py:data:: JSON_AS_ASCII + + Serialize objects to ASCII-encoded JSON. If this is disabled, the JSON + will be returned as a Unicode string, or encoded as ``UTF-8`` by + ``jsonify``. This has security implications when rendering the JSON in + to JavaScript in templates, and should typically remain enabled. + + Default: ``True`` + +.. py:data:: JSON_SORT_KEYS + + Sort the keys of JSON objects alphabetically. This is useful for caching + because it ensures the data is serialized the same way no matter what + Python's hash seed is. While not recommended, you can disable this for a + possible performance improvement at the cost of caching. + + Default: ``True`` + +.. py:data:: JSONIFY_PRETTYPRINT_REGULAR + + ``jsonify`` responses will be output with newlines, spaces, and indentation + for easier reading by humans. Always enabled in debug mode. + + Default: ``False`` + +.. py:data:: JSONIFY_MIMETYPE + + The mimetype of ``jsonify`` responses. + + Default: ``'application/json'`` + +.. py:data:: TEMPLATES_AUTO_RELOAD + + Reload templates when they are changed. If not set, it will be enabled in + debug mode. + + Default: ``None`` + +.. py:data:: EXPLAIN_TEMPLATE_LOADING + + Log debugging information tracing how a template file was loaded. This can + be useful to figure out why a template was not loaded or the wrong file + appears to be loaded. + + Default: ``False`` .. versionadded:: 0.4 ``LOGGER_NAME`` @@ -477,3 +545,4 @@ Example usage for both:: # or via open_instance_resource: with app.open_instance_resource('application.cfg') as f: config = f.read() + From 4a53840df00c3338e42afbf78811382a0d232484 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 28 May 2017 14:08:53 -0700 Subject: [PATCH 256/399] APPLICATION_ROOT defaults to '/' --- CHANGES | 3 +++ flask/app.py | 4 ++-- flask/sessions.py | 4 ++-- flask/testing.py | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index e41028c8..a451f2a6 100644 --- a/CHANGES +++ b/CHANGES @@ -49,6 +49,7 @@ Major release, unreleased work with the ``flask`` command. If they take a single parameter or a parameter named ``script_info``, the ``ScriptInfo`` object will be passed. (`#2319`_) +- FLASK_APP=myproject.app:create_app('dev') support. - ``FLASK_APP`` can be set to an app factory, with arguments if needed, for example ``FLASK_APP=myproject.app:create_app('dev')``. (`#2326`_) - ``View.provide_automatic_options = True`` is set on the view function from @@ -59,6 +60,8 @@ Major release, unreleased accessed at all during the request (and it wasn't deleted). (`#2288`_) - ``app.test_request_context()`` take ``subdomain`` and ``url_scheme`` parameters for use when building base URL. (`#1621`_) +- Set ``APPLICATION_ROOT = '/'`` by default. This was already the implicit + default when it was set to ``None``. .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 diff --git a/flask/app.py b/flask/app.py index efcc8ee0..5e25d018 100644 --- a/flask/app.py +++ b/flask/app.py @@ -307,7 +307,7 @@ class Flask(_PackageBoundObject): 'LOGGER_NAME': None, 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, - 'APPLICATION_ROOT': None, + 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', 'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_PATH': None, @@ -1845,7 +1845,7 @@ class Flask(_PackageBoundObject): if self.config['SERVER_NAME'] is not None: return self.url_map.bind( self.config['SERVER_NAME'], - script_name=self.config['APPLICATION_ROOT'] or '/', + script_name=self.config['APPLICATION_ROOT'], url_scheme=self.config['PREFERRED_URL_SCHEME']) def inject_url_defaults(self, endpoint, values): diff --git a/flask/sessions.py b/flask/sessions.py index 47f0b3fd..a4b8cad1 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -288,8 +288,8 @@ class SessionInterface(object): config var if it's set, and falls back to ``APPLICATION_ROOT`` or uses ``/`` if it's ``None``. """ - return app.config['SESSION_COOKIE_PATH'] or \ - app.config['APPLICATION_ROOT'] or '/' + return app.config['SESSION_COOKIE_PATH'] \ + or app.config['APPLICATION_ROOT'] def get_cookie_httponly(self, app): """Returns True if the session cookie should be httponly. This diff --git a/flask/testing.py b/flask/testing.py index 86e433bc..99dbd973 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -34,13 +34,13 @@ def make_test_environ_builder( if base_url is None: http_host = app.config.get('SERVER_NAME') or 'localhost' - app_root = app.config.get('APPLICATION_ROOT') or '/' + app_root = app.config['APPLICATION_ROOT'] if subdomain: http_host = '{0}.{1}'.format(subdomain, http_host) if url_scheme is None: - url_scheme = app.config.get('PREFERRED_URL_SCHEME') or 'http' + url_scheme = app.config['PREFERRED_URL_SCHEME'] url = url_parse(path) base_url = '{0}://{1}/{2}'.format( From b8eba0a3fa940f85d43aa2a26bd31a75a446eb28 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 29 May 2017 10:09:24 -0700 Subject: [PATCH 257/399] use existing response.vary property to set vary header closes #2345 --- flask/helpers.py | 14 -------------- flask/sessions.py | 3 +-- tests/test_basic.py | 21 +++++++++++---------- 3 files changed, 12 insertions(+), 26 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index 2bcdc10b..f37be677 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -1004,17 +1004,3 @@ def is_ip(value): return True return False - - -def patch_vary_header(response, value): - """Add a value to the ``Vary`` header if it is not already present.""" - - header = response.headers.get('Vary', '') - headers = [h for h in (h.strip() for h in header.split(',')) if h] - lower_value = value.lower() - - if not any(h.lower() == lower_value for h in headers): - headers.append(value) - - updated_header = ', '.join(headers) - response.headers['Vary'] = updated_header diff --git a/flask/sessions.py b/flask/sessions.py index a4b8cad1..3a5e2ac3 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -19,7 +19,6 @@ from itsdangerous import BadSignature, URLSafeTimedSerializer from werkzeug.datastructures import CallbackDict from werkzeug.http import http_date, parse_date -from flask.helpers import patch_vary_header from . import Markup, json from ._compat import iteritems, text_type from .helpers import is_ip, total_seconds @@ -407,7 +406,7 @@ class SecureCookieSessionInterface(SessionInterface): # Add a "Vary: Cookie" header if the session was accessed at all. if session.accessed: - patch_vary_header(response, 'Cookie') + response.vary.add('Cookie') if not self.should_set_cookie(app, session): return diff --git a/tests/test_basic.py b/tests/test_basic.py index e4d6b2f9..895f4f30 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -9,20 +9,21 @@ :license: BSD, see LICENSE for more details. """ -import pytest - +import pickle import re -import uuid import time -import flask -import pickle +import uuid from datetime import datetime from threading import Thread -from flask._compat import text_type -from werkzeug.exceptions import BadRequest, NotFound, Forbidden + +import pytest +import werkzeug.serving +from werkzeug.exceptions import BadRequest, Forbidden, NotFound from werkzeug.http import parse_date from werkzeug.routing import BuildError -import werkzeug.serving + +import flask +from flask._compat import text_type def test_options_work(app, client): @@ -523,14 +524,14 @@ def test_session_vary_cookie(app, client): @app.route('/vary-cookie-header-set') def vary_cookie_header_set(): response = flask.Response() - response.headers['Vary'] = 'Cookie' + response.vary.add('Cookie') flask.session['test'] = 'test' return response @app.route('/vary-header-set') def vary_header_set(): response = flask.Response() - response.headers['Vary'] = 'Accept-Encoding, Accept-Language' + response.vary.update(('Accept-Encoding', 'Accept-Language')) flask.session['test'] = 'test' return response From abf54c8182b052db3173d50ba449b6ea9bd40b2b Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 29 May 2017 10:29:06 -0700 Subject: [PATCH 258/399] fix some config names in new doc --- docs/config.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 9fffa09f..639d1d5a 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -163,7 +163,7 @@ The following configuration values are used internally by Flask: Default: ``False`` -.. py:data:: SESSION_COOKIE_LIFETIME +.. py:data:: PERMANENT_SESSION_LIFETIME If ``session.permanent`` is true, the cookie's max age will be set to this number of seconds. Can either be a :class:`datetime.timedelta` or an @@ -188,7 +188,7 @@ The following configuration values are used internally by Flask: Default: ``False`` -.. py:data:: SEND_FILE_MAX_AGE +.. py:data:: SEND_FILE_MAX_AGE_DEFAULT When serving files, set the cache control max age to this number of seconds. Can either be a :class:`datetime.timedelta` or an ``int``. From b9c8c9bad124790ff0a8a9615d3d40a3b6fe2d83 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 29 May 2017 13:30:58 -0700 Subject: [PATCH 259/399] deprecate app session methods in favor of session_interface ref #1182 --- flask/app.py | 30 +++++++++++++++++++++++++++++- flask/ctx.py | 6 ++++-- flask/testing.py | 7 ++++--- 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/flask/app.py b/flask/app.py index 5e25d018..f8f37027 100644 --- a/flask/app.py +++ b/flask/app.py @@ -10,6 +10,7 @@ """ import os import sys +import warnings from datetime import timedelta from functools import update_wrapper from itertools import chain @@ -925,8 +926,17 @@ class Flask(_PackageBoundObject): :attr:`secret_key` is set. Instead of overriding this method we recommend replacing the :class:`session_interface`. + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.open_session`` + instead. + :param request: an instance of :attr:`request_class`. """ + + warnings.warn(DeprecationWarning( + '"open_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.open_session" instead.' + )) return self.session_interface.open_session(self, request) def save_session(self, session, response): @@ -934,19 +944,37 @@ class Flask(_PackageBoundObject): implementation, check :meth:`open_session`. Instead of overriding this method we recommend replacing the :class:`session_interface`. + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.save_session`` + instead. + :param session: the session to be saved (a :class:`~werkzeug.contrib.securecookie.SecureCookie` object) :param response: an instance of :attr:`response_class` """ + + warnings.warn(DeprecationWarning( + '"save_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.save_session" instead.' + )) return self.session_interface.save_session(self, session, response) def make_null_session(self): """Creates a new instance of a missing session. Instead of overriding this method we recommend replacing the :class:`session_interface`. + .. deprecated: 1.0 + Will be removed in 1.1. Use ``session_interface.make_null_session`` + instead. + .. versionadded:: 0.7 """ + + warnings.warn(DeprecationWarning( + '"make_null_session" is deprecated and will be removed in 1.1. Use' + ' "session_interface.make_null_session" instead.' + )) return self.session_interface.make_null_session(self) @setupmethod @@ -1932,7 +1960,7 @@ class Flask(_PackageBoundObject): for handler in funcs: response = handler(response) if not self.session_interface.is_null_session(ctx.session): - self.save_session(ctx.session, response) + self.session_interface.save_session(self, ctx.session, response) return response def do_teardown_request(self, exc=_sentinel): diff --git a/flask/ctx.py b/flask/ctx.py index 480d9c5c..70743de6 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -329,9 +329,11 @@ class RequestContext(object): # available. This allows a custom open_session method to use the # request context (e.g. code that access database information # stored on `g` instead of the appcontext). - self.session = self.app.open_session(self.request) + session_interface = self.app.session_interface + self.session = session_interface.open_session(self.app, self.request) + if self.session is None: - self.session = self.app.make_null_session() + self.session = session_interface.make_null_session(self.app) def pop(self, exc=_sentinel): """Pops the request context and unbinds it by doing that. This will diff --git a/flask/testing.py b/flask/testing.py index 99dbd973..5f9269f2 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -105,7 +105,8 @@ class FlaskClient(Client): self.cookie_jar.inject_wsgi(environ_overrides) outer_reqctx = _request_ctx_stack.top with app.test_request_context(*args, **kwargs) as c: - sess = app.open_session(c.request) + session_interface = app.session_interface + sess = session_interface.open_session(app, c.request) if sess is None: raise RuntimeError('Session backend did not open a session. ' 'Check the configuration') @@ -124,8 +125,8 @@ class FlaskClient(Client): _request_ctx_stack.pop() resp = app.response_class() - if not app.session_interface.is_null_session(sess): - app.save_session(sess, resp) + if not session_interface.is_null_session(sess): + session_interface.save_session(app, sess, resp) headers = resp.get_wsgi_headers(c.request.environ) self.cookie_jar.extract_wsgi(c.request.environ, headers) From 045dccaefbb2582c260e324aebaec425d355b725 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 29 May 2017 19:08:25 -0700 Subject: [PATCH 260/399] make debugging bad key errors easier * TRAP_BAD_REQUEST_ERRORS is enabled by default in debug mode * BadRequestKeyError has the key in the description in debug mode closes #382 --- CHANGES | 6 +++++- docs/config.rst | 5 +++-- flask/app.py | 31 +++++++++++++++++++++++++++---- tests/test_helpers.py | 2 ++ 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/CHANGES b/CHANGES index a451f2a6..dc39a95d 100644 --- a/CHANGES +++ b/CHANGES @@ -49,7 +49,7 @@ Major release, unreleased work with the ``flask`` command. If they take a single parameter or a parameter named ``script_info``, the ``ScriptInfo`` object will be passed. (`#2319`_) -- FLASK_APP=myproject.app:create_app('dev') support. +- FLASK_APP=myproject.app:create_app('dev') support. - ``FLASK_APP`` can be set to an app factory, with arguments if needed, for example ``FLASK_APP=myproject.app:create_app('dev')``. (`#2326`_) - ``View.provide_automatic_options = True`` is set on the view function from @@ -62,6 +62,9 @@ Major release, unreleased parameters for use when building base URL. (`#1621`_) - Set ``APPLICATION_ROOT = '/'`` by default. This was already the implicit default when it was set to ``None``. +- ``TRAP_BAD_REQUEST_ERRORS`` is enabled by default in debug mode. + ``BadRequestKeyError`` has a message with the bad key in debug mode instead + of the generic bad request message. (`#2348`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -80,6 +83,7 @@ Major release, unreleased .. _#2316: https://github.com/pallets/flask/pull/2316 .. _#2319: https://github.com/pallets/flask/pull/2319 .. _#2326: https://github.com/pallets/flask/pull/2326 +.. _#2348: https://github.com/pallets/flask/pull/2348 Version 0.12.2 -------------- diff --git a/docs/config.rst b/docs/config.rst index 639d1d5a..1da7d2ee 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -109,9 +109,10 @@ The following configuration values are used internally by Flask: Trying to access a key that doesn't exist from request dicts like ``args`` and ``form`` will return a 400 Bad Request error page. Enable this to treat the error as an unhandled exception instead so that you get the interactive - debugger. This is a more specific version of ``TRAP_HTTP_EXCEPTIONS``. + debugger. This is a more specific version of ``TRAP_HTTP_EXCEPTIONS``. If + unset, it is enabled in debug mode. - Default: ``False`` + Default: ``None`` .. py:data:: SECRET_KEY diff --git a/flask/app.py b/flask/app.py index f8f37027..6ddf6ca9 100644 --- a/flask/app.py +++ b/flask/app.py @@ -18,7 +18,8 @@ from threading import Lock from werkzeug.datastructures import ImmutableDict, Headers from werkzeug.exceptions import BadRequest, HTTPException, \ - InternalServerError, MethodNotAllowed, default_exceptions + InternalServerError, MethodNotAllowed, default_exceptions, \ + BadRequestKeyError from werkzeug.routing import BuildError, Map, RequestRedirect, Rule from . import cli, json @@ -317,7 +318,7 @@ class Flask(_PackageBoundObject): 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), - 'TRAP_BAD_REQUEST_ERRORS': False, + 'TRAP_BAD_REQUEST_ERRORS': None, 'TRAP_HTTP_EXCEPTIONS': False, 'EXPLAIN_TEMPLATE_LOADING': False, 'PREFERRED_URL_SCHEME': 'http', @@ -1542,13 +1543,21 @@ class Flask(_PackageBoundObject): exception is not called and it shows up as regular exception in the traceback. This is helpful for debugging implicitly raised HTTP exceptions. + + .. versionchanged:: 1.0 + Bad request errors are not trapped by default in debug mode. .. versionadded:: 0.8 """ if self.config['TRAP_HTTP_EXCEPTIONS']: return True - if self.config['TRAP_BAD_REQUEST_ERRORS']: + + trap_bad_request = self.config['TRAP_BAD_REQUEST_ERRORS'] + + # if unset, trap based on debug mode + if (trap_bad_request is None and self.debug) or trap_bad_request: return isinstance(e, BadRequest) + return False def handle_user_exception(self, e): @@ -1559,16 +1568,30 @@ class Flask(_PackageBoundObject): function will either return a response value or reraise the exception with the same traceback. + .. versionchanged:: 1.0 + Key errors raised from request data like ``form`` show the the bad + key in debug mode rather than a generic bad request message. + .. versionadded:: 0.7 """ exc_type, exc_value, tb = sys.exc_info() assert exc_value is e - # ensure not to trash sys.exc_info() at that point in case someone # 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. + # MultiDict passes the key to the exception, but that's ignored + # when generating the response message. Set an informative + # description for key errors in debug mode. + if ( + self.debug + and isinstance(e, BadRequestKeyError) + # only set it if it's still the default description + and e.description is BadRequestKeyError.description + ): + e.description = "KeyError: '{0}'".format(*e.args) + if isinstance(e, HTTPException) and not self.trap_http_exception(e): return self.handle_http_exception(e) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index a67fed0b..a2d22dd6 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -44,6 +44,7 @@ class TestJSON(object): def test_post_empty_json_adds_exception_to_response_content_in_debug(self, app, client): app.config['DEBUG'] = True + app.config['TRAP_BAD_REQUEST_ERRORS'] = False @app.route('/json', methods=['POST']) def post_json(): @@ -56,6 +57,7 @@ class TestJSON(object): def test_post_empty_json_wont_add_exception_to_response_if_no_debug(self, app, client): app.config['DEBUG'] = False + app.config['TRAP_BAD_REQUEST_ERRORS'] = False @app.route('/json', methods=['POST']) def post_json(): From 42905b8a55adf66a77b2b11797092a64b51d526e Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 29 May 2017 19:41:07 -0700 Subject: [PATCH 261/399] set description for trap as well as debug test for key error description --- flask/app.py | 4 ++-- tests/test_basic.py | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/flask/app.py b/flask/app.py index 6ddf6ca9..faf36029 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1583,9 +1583,9 @@ class Flask(_PackageBoundObject): # MultiDict passes the key to the exception, but that's ignored # when generating the response message. Set an informative - # description for key errors in debug mode. + # description for key errors in debug mode or when trapping errors. if ( - self.debug + self.debug or self.config['TRAP_BAD_REQUEST_ERRORS'] and isinstance(e, BadRequestKeyError) # only set it if it's still the default description and e.description is BadRequestKeyError.description diff --git a/tests/test_basic.py b/tests/test_basic.py index 895f4f30..c494e8bd 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -975,12 +975,17 @@ def test_trapping_of_bad_request_key_errors(app, client): def fail(): flask.request.form['missing_key'] - assert client.get('/fail').status_code == 400 + rv = client.get('/fail') + assert rv.status_code == 400 + assert b'missing_key' not in rv.data app.config['TRAP_BAD_REQUEST_ERRORS'] = True + with pytest.raises(KeyError) as e: client.get("/fail") + assert e.errisinstance(BadRequest) + assert 'missing_key' in e.value.description def test_trapping_of_all_http_exceptions(app, client): From 8f3563cf79024056987864ad4229f03e6c1aec99 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 29 May 2017 19:46:33 -0700 Subject: [PATCH 262/399] fix operator precedence --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index faf36029..3c6d9f54 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1585,7 +1585,7 @@ class Flask(_PackageBoundObject): # when generating the response message. Set an informative # description for key errors in debug mode or when trapping errors. if ( - self.debug or self.config['TRAP_BAD_REQUEST_ERRORS'] + (self.debug or self.config['TRAP_BAD_REQUEST_ERRORS']) and isinstance(e, BadRequestKeyError) # only set it if it's still the default description and e.description is BadRequestKeyError.description From 859d9a9d5c3da114132ec9f0e515e1fa8030f68f Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 31 May 2017 18:02:23 -0700 Subject: [PATCH 263/399] show nice message when registering error handler for unknown code clean up error handler docs closes #1837 --- docs/errorhandling.rst | 71 ++++++++++++++++++++++++++++-------------- flask/app.py | 55 +++++++++++++++----------------- tests/test_basic.py | 7 +++++ 3 files changed, 79 insertions(+), 54 deletions(-) diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst index 2791fec3..84c649ce 100644 --- a/docs/errorhandling.rst +++ b/docs/errorhandling.rst @@ -76,49 +76,72 @@ Error handlers You might want to show custom error pages to the user when an error occurs. This can be done by registering error handlers. -Error handlers are normal :ref:`views` but instead of being registered for -routes, they are registered for exceptions that are raised while trying to -do something else. +An error handler is a normal view function that return a response, but instead +of being registered for a route, it is registered for an exception or HTTP +status code that would is raised while trying to handle a request. Registering ``````````` -Register error handlers using :meth:`~flask.Flask.errorhandler` or -:meth:`~flask.Flask.register_error_handler`:: +Register handlers by decorating a function with +:meth:`~flask.Flask.errorhandler`. Or use +:meth:`~flask.Flask.register_error_handler` to register the function later. +Remember to set the error code when returning the response. :: @app.errorhandler(werkzeug.exceptions.BadRequest) def handle_bad_request(e): - return 'bad request!' + return 'bad request!', 400 - app.register_error_handler(400, lambda e: 'bad request!') + # or, without the decorator + app.register_error_handler(400, handle_bad_request) -Those two ways are equivalent, but the first one is more clear and leaves -you with a function to call on your whim (and in tests). Note that :exc:`werkzeug.exceptions.HTTPException` subclasses like -:exc:`~werkzeug.exceptions.BadRequest` from the example and their HTTP codes -are interchangeable when handed to the registration methods or decorator -(``BadRequest.code == 400``). +:exc:`~werkzeug.exceptions.BadRequest` and their HTTP codes are interchangeable +when registering handlers. (``BadRequest.code == 400``) -You are however not limited to :exc:`~werkzeug.exceptions.HTTPException` -or HTTP status codes but can register a handler for every exception class you -like. +Non-standard HTTP codes cannot be registered by code because they are not known +by Werkzeug. Instead, define a subclass of +:class:`~werkzeug.exceptions.HTTPException` with the appropriate code and +register and raise that exception class. :: -.. versionchanged:: 0.11 + class InsufficientStorage(werkzeug.exceptions.HTTPException): + code = 507 + description = 'Not enough storage space.' + + app.register_error_handler(InsuffcientStorage, handle_507) - Errorhandlers are now prioritized by specificity of the exception classes - they are registered for instead of the order they are registered in. + raise InsufficientStorage() + +Handlers can be registered for any exception class, not just +:exc:`~werkzeug.exceptions.HTTPException` subclasses or HTTP status +codes. Handlers can be registered for a specific class, or for all subclasses +of a parent class. Handling ```````` -Once an exception instance is raised, its class hierarchy is traversed, -and searched for in the exception classes for which handlers are registered. -The most specific handler is selected. +When an exception is caught by Flask while handling a request, it is first +looked up by code. If no handler is registered for the code, it is looked up +by its class hierarchy; the most specific handler is chosen. If no handler is +registered, :class:`~werkzeug.exceptions.HTTPException` subclasses show a +generic message about their code, while other exceptions are converted to a +generic 500 Internal Server Error. -E.g. if an instance of :exc:`ConnectionRefusedError` is raised, and a handler +For example, if an instance of :exc:`ConnectionRefusedError` is raised, and a handler is registered for :exc:`ConnectionError` and :exc:`ConnectionRefusedError`, -the more specific :exc:`ConnectionRefusedError` handler is called on the -exception instance, and its response is shown to the user. +the more specific :exc:`ConnectionRefusedError` handler is called with the +exception instance to generate the response. + +Handlers registered on the blueprint take precedence over those registered +globally on the application, assuming a blueprint is handling the request that +raises the exception. However, the blueprint cannot handle 404 routing errors +because the 404 occurs at the routing level before the blueprint can be +determined. + +.. versionchanged:: 0.11 + + Handlers are prioritized by specificity of the exception classes they are + registered for instead of the order they are registered in. Error Mails ----------- diff --git a/flask/app.py b/flask/app.py index 3c6d9f54..342dde86 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1166,7 +1166,9 @@ class Flask(_PackageBoundObject): @setupmethod def errorhandler(self, code_or_exception): - """A decorator that is used to register a function given an + """Register a function to handle errors by code or exception class. + + A decorator that is used to register a function given an error code. Example:: @app.errorhandler(404) @@ -1179,21 +1181,6 @@ class Flask(_PackageBoundObject): def special_exception_handler(error): return 'Database connection failed', 500 - You can also register a function as error handler without using - the :meth:`errorhandler` decorator. The following example is - equivalent to the one above:: - - def page_not_found(error): - return 'This page does not exist', 404 - app.error_handler_spec[None][404] = page_not_found - - Setting error handlers via assignments to :attr:`error_handler_spec` - however is discouraged as it requires fiddling with nested dictionaries - and the special case for arbitrary exception types. - - 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 @@ -1212,6 +1199,7 @@ class Flask(_PackageBoundObject): return f return decorator + @setupmethod def register_error_handler(self, code_or_exception, f): """Alternative error attach function to the :meth:`errorhandler` decorator that is more straightforward to use for non decorator @@ -1230,11 +1218,18 @@ class Flask(_PackageBoundObject): """ if isinstance(code_or_exception, HTTPException): # old broken behavior raise ValueError( - 'Tried to register a handler for an exception instance {0!r}. ' - 'Handlers can only be registered for exception classes or HTTP error codes.' - .format(code_or_exception)) + 'Tried to register a handler for an exception instance {0!r}.' + ' Handlers can only be registered for exception classes or' + ' HTTP error codes.'.format(code_or_exception) + ) - exc_class, code = self._get_exc_class_and_code(code_or_exception) + try: + exc_class, code = self._get_exc_class_and_code(code_or_exception) + except KeyError: + raise KeyError( + "'{0}' is not a recognized HTTP error code. Use a subclass of" + " HTTPException with that code instead.".format(code_or_exception) + ) handlers = self.error_handler_spec.setdefault(key, {}).setdefault(code, {}) handlers[exc_class] = f @@ -1339,7 +1334,7 @@ class Flask(_PackageBoundObject): @setupmethod def before_request(self, f): """Registers a function to run before each request. - + For example, this can be used to open a database connection, or to load the logged in user from the session. @@ -1467,12 +1462,12 @@ class Flask(_PackageBoundObject): """Register a URL value preprocessor function for all view functions in the application. These functions will be called before the :meth:`before_request` functions. - + The function can modify the values captured from the matched url before they are passed to the view. For example, this can be used to pop a common language code value and place it in ``g`` rather than pass it to every view. - + The function is passed the endpoint name and values dict. The return value is ignored. """ @@ -1543,7 +1538,7 @@ class Flask(_PackageBoundObject): exception is not called and it shows up as regular exception in the traceback. This is helpful for debugging implicitly raised HTTP exceptions. - + .. versionchanged:: 1.0 Bad request errors are not trapped by default in debug mode. @@ -1783,10 +1778,10 @@ class Flask(_PackageBoundObject): ``str`` (``unicode`` in Python 2) A response object is created with the string encoded to UTF-8 as the body. - + ``bytes`` (``str`` in Python 2) A response object is created with the bytes as the body. - + ``tuple`` Either ``(body, status, headers)``, ``(body, status)``, or ``(body, headers)``, where ``body`` is any of the other types @@ -1795,13 +1790,13 @@ class Flask(_PackageBoundObject): tuples. If ``body`` is a :attr:`response_class` instance, ``status`` overwrites the exiting value and ``headers`` are extended. - + :attr:`response_class` The object is returned unchanged. - + other :class:`~werkzeug.wrappers.Response` class The object is coerced to :attr:`response_class`. - + :func:`callable` The function is called as a WSGI application. The result is used to create a response object. @@ -1938,7 +1933,7 @@ class Flask(_PackageBoundObject): :attr:`url_value_preprocessors` registered with the app and the current blueprint (if any). Then calls :attr:`before_request_funcs` registered with the app and the blueprint. - + If any :meth:`before_request` handler returns a non-None value, the value is handled as if it was the return value from the view, and further request handling is stopped. diff --git a/tests/test_basic.py b/tests/test_basic.py index c494e8bd..a38428b2 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -870,6 +870,13 @@ def test_error_handling(app, client): assert b'forbidden' == rv.data +def test_error_handler_unknown_code(app): + with pytest.raises(KeyError) as exc_info: + app.register_error_handler(999, lambda e: ('999', 999)) + + assert 'Use a subclass' in exc_info.value.args[0] + + def test_error_handling_processing(app, client): app.config['LOGGER_HANDLER_POLICY'] = 'never' app.testing = False From 5e1ced3c055f7eb567bf7266c98de3d44ceea1b4 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 1 Jun 2017 22:47:23 -0700 Subject: [PATCH 264/399] make session serializer extensible support serializing 1-item dicts with tag as key refactor serializer into flask.json.tag module continues #1452, closes #1438, closes #1908 --- CHANGES | 3 + flask/{json.py => json/__init__.py} | 13 +- flask/json/tag.py | 188 ++++++++++++++++++++++++++++ flask/sessions.py | 137 ++------------------ 4 files changed, 201 insertions(+), 140 deletions(-) rename flask/{json.py => json/__init__.py} (97%) create mode 100644 flask/json/tag.py diff --git a/CHANGES b/CHANGES index dc39a95d..a3504ad5 100644 --- a/CHANGES +++ b/CHANGES @@ -65,6 +65,8 @@ Major release, unreleased - ``TRAP_BAD_REQUEST_ERRORS`` is enabled by default in debug mode. ``BadRequestKeyError`` has a message with the bad key in debug mode instead of the generic bad request message. (`#2348`_) +- Allow registering new tags with ``TaggedJSONSerializer`` to support + storing other types in the session cookie. (`#2352`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -84,6 +86,7 @@ Major release, unreleased .. _#2319: https://github.com/pallets/flask/pull/2319 .. _#2326: https://github.com/pallets/flask/pull/2326 .. _#2348: https://github.com/pallets/flask/pull/2348 +.. _#2352: https://github.com/pallets/flask/pull/2352 Version 0.12.2 -------------- diff --git a/flask/json.py b/flask/json/__init__.py similarity index 97% rename from flask/json.py rename to flask/json/__init__.py index a029e73a..93e6fdc4 100644 --- a/flask/json.py +++ b/flask/json/__init__.py @@ -1,18 +1,9 @@ # -*- coding: utf-8 -*- -""" - flask.json - ~~~~~~~~~~ - - Implementation helpers for the JSON support in Flask. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" import io import uuid from datetime import date -from .globals import current_app, request -from ._compat import text_type, PY2 +from flask.globals import current_app, request +from flask._compat import text_type, PY2 from werkzeug.http import http_date from jinja2 import Markup diff --git a/flask/json/tag.py b/flask/json/tag.py new file mode 100644 index 00000000..40594282 --- /dev/null +++ b/flask/json/tag.py @@ -0,0 +1,188 @@ +from base64 import b64decode, b64encode +from datetime import datetime +from uuid import UUID + +from jinja2 import Markup +from werkzeug.http import http_date, parse_date + +from flask._compat import iteritems, text_type +from flask.json import dumps, loads + + +class JSONTag(object): + __slots__ = () + key = None + + def check(self, serializer, value): + raise NotImplementedError + + def to_json(self, serializer, value): + raise NotImplementedError + + def to_python(self, serializer, value): + raise NotImplementedError + + def tag(self, serializer, value): + return {self.key: self.to_json(serializer, value)} + + +class TagDict(JSONTag): + __slots__ = () + key = ' di' + + def check(self, serializer, value): + return isinstance(value, dict) + + def to_json(self, serializer, value, key=None): + if key is not None: + return {key + '__': serializer._tag(value[key])} + + return dict((k, serializer._tag(v)) for k, v in iteritems(value)) + + def to_python(self, serializer, value): + key = next(iter(value)) + return {key[:-2]: value[key]} + + def tag(self, serializer, value): + if len(value) == 1: + key = next(iter(value)) + + if key in serializer._tags: + return {self.key: self.to_json(serializer, value, key=key)} + + return self.to_json(serializer, value) + + +class TagTuple(JSONTag): + __slots__ = () + key = ' t' + + def check(self, serializer, value): + return isinstance(value, tuple) + + def to_json(self, serializer, value): + return [serializer._tag(item) for item in value] + + def to_python(self, serializer, value): + return tuple(value) + + +class PassList(JSONTag): + __slots__ = () + + def check(self, serializer, value): + return isinstance(value, list) + + def to_json(self, serializer, value): + return [serializer._tag(item) for item in value] + + tag = to_json + + +class TagBytes(JSONTag): + __slots__ = () + key = ' b' + + def check(self, serializer, value): + return isinstance(value, bytes) + + def to_json(self, serializer, value): + return b64encode(value).decode('ascii') + + def to_python(self, serializer, value): + return b64decode(value) + + +class TagMarkup(JSONTag): + __slots__ = () + key = ' m' + + def check(self, serializer, value): + return callable(getattr(value, '__html__', None)) + + def to_json(self, serializer, value): + return text_type(value.__html__()) + + def to_python(self, serializer, value): + return Markup(value) + + +class TagUUID(JSONTag): + __slots__ = () + key = ' u' + + def check(self, serializer, value): + return isinstance(value, UUID) + + def to_json(self, serializer, value): + return value.hex + + def to_python(self, serializer, value): + return UUID(value) + + +class TagDateTime(JSONTag): + __slots__ = () + key = ' d' + + def check(self, serializer, value): + return isinstance(value, datetime) + + def to_json(self, serializer, value): + return http_date(value) + + def to_python(self, serializer, value): + return parse_date(value) + + +class TaggedJSONSerializer(object): + __slots__ = ('_tags', '_order') + _default_tags = [ + TagDict(), TagTuple(), PassList(), TagBytes(), TagMarkup(), TagUUID(), + TagDateTime(), + ] + + def __init__(self): + self._tags = {} + self._order = [] + + for tag in self._default_tags: + self.register(tag) + + def register(self, tag, force=False, index=-1): + key = tag.key + + if key is not None: + if not force and key in self._tags: + raise KeyError("Tag '{0}' is already registered.".format(key)) + + self._tags[key] = tag + + if index == -1: + self._order.append(tag) + else: + self._order.insert(index, tag) + + def _tag(self, value): + for tag in self._order: + if tag.check(self, value): + return tag.tag(self, value) + + return value + + def _untag(self, value): + if len(value) != 1: + return value + + key = next(iter(value)) + + if key not in self._tags: + return value + + return self._tags[key].to_python(self, value[key]) + + def dumps(self, value): + return dumps(self._tag(value), separators=(',', ':')) + + def loads(self, value): + return loads(value, object_hook=self._untag) diff --git a/flask/sessions.py b/flask/sessions.py index a334e703..82b588bc 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -9,18 +9,14 @@ :license: BSD, see LICENSE for more details. """ import hashlib -import uuid import warnings -from base64 import b64decode, b64encode from datetime import datetime from itsdangerous import BadSignature, URLSafeTimedSerializer from werkzeug.datastructures import CallbackDict -from werkzeug.http import http_date, parse_date -from . import Markup, json -from ._compat import iteritems, text_type -from .helpers import is_ip, total_seconds +from flask.helpers import is_ip, total_seconds +from flask.json.tag import TaggedJSONSerializer class SessionMixin(object): @@ -57,126 +53,6 @@ class SessionMixin(object): #: from being served the same cache. accessed = True -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 __init__(self): - self.conversions = [ - { - 'check': lambda value: self._is_dict_with_used_key(value), - 'tag': lambda value: self._tag_dict_used_with_key(value), - 'untag': lambda value: self._untag_dict_used_with_key(value), - 'key': ' di', - }, - { - 'check': lambda value: isinstance(value, tuple), - 'tag': lambda value: [self._tag(x) for x in value], - 'untag': lambda value: tuple(value), - 'key': ' t', - }, - { - 'check': lambda value: isinstance(value, uuid.UUID), - 'tag': lambda value: value.hex, - 'untag': lambda value: uuid.UUID(value), - 'key': ' u', - }, - { - 'check': lambda value: isinstance(value, bytes), - 'tag': lambda value: b64encode(value).decode('ascii'), - 'untag': lambda value: b64decode(value), - 'key': ' b', - }, - { - 'check': lambda value: callable(getattr(value, '__html__', - None)), - 'tag': lambda value: text_type(value.__html__()), - 'untag': lambda value: Markup(value), - 'key': ' m', - }, - { - 'check': lambda value: isinstance(value, list), - 'tag': lambda value: [self._tag(x) for x in value], - }, - { - 'check': lambda value: isinstance(value, datetime), - 'tag': lambda value: http_date(value), - 'untag': lambda value: parse_date(value), - 'key': ' d', - }, - { - 'check': lambda value: isinstance(value, dict), - 'tag': lambda value: dict((k, self._tag(v)) for k, v in - iteritems(value)), - }, - { - 'check': lambda value: isinstance(value, str), - 'tag': lambda value: self._tag_string(value), - } - ] - - @property - def keys(self): - return [c['key'] for c in self.conversions if c.get('key')] - - def _get_conversion_untag(self, key): - return next( - (c['untag'] for c in self.conversions if c.get('key') == key), - lambda v: None - ) - - def _is_dict_with_used_key(self, v): - return isinstance(v, dict) and len(v) == 1 and list(v)[0] in self.keys - - def _was_dict_with_used_key(self, k): - return k.endswith('__') and k[:-2] in self.keys - - def _tag_string(self, value): - try: - return text_type(value) - except UnicodeError: - from flask.debughelpers import UnexpectedUnicodeError - 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) - - def _tag_dict_used_with_key(self, value): - k, v = next(iteritems(value)) - return {'%s__' % k: v} - - def _tag(self, value): - for tag_ops in self.conversions: - if tag_ops['check'](value): - tag = tag_ops.get('key') - if tag: - return {tag: tag_ops['tag'](value)} - return tag_ops['tag'](value) - return value - - def _untag_dict_used_with_key(self, the_value): - k, v = next(iteritems(the_value)) - if self._was_dict_with_used_key(k): - return {k[:-2]: self._untag(v)} - - def _untag(self, obj): - if len(obj) != 1: - return obj - the_key, the_value = next(iteritems(obj)) - untag = self._get_conversion_untag(the_key) - new_value = untag(the_value) - return new_value if new_value else obj - - def dumps(self, value): - return json.dumps(self._tag(value), separators=(',', ':')) - - def loads(self, value): - return json.loads(value, object_hook=self._untag) - - -session_json_serializer = TaggedJSONSerializer() - class SecureCookieSession(CallbackDict, SessionMixin): """Base class for sessions based on signed cookies.""" @@ -284,10 +160,10 @@ class SessionInterface(object): def get_cookie_domain(self, app): """Returns the domain that should be set for the session cookie. - + Uses ``SESSION_COOKIE_DOMAIN`` if it is configured, otherwise falls back to detecting the domain based on ``SERVER_NAME``. - + Once detected (or if not set at all), ``SESSION_COOKIE_DOMAIN`` is updated to avoid re-running the logic. """ @@ -377,7 +253,7 @@ class SessionInterface(object): has been modified, the cookie is set. If the session is permanent and the ``SESSION_REFRESH_EACH_REQUEST`` config is true, the cookie is always set. - + This check is usually skipped if the session was deleted. .. versionadded:: 0.11 @@ -404,6 +280,9 @@ class SessionInterface(object): raise NotImplementedError() +session_json_serializer = TaggedJSONSerializer() + + class SecureCookieSessionInterface(SessionInterface): """The default session interface that stores sessions in signed cookies through the :mod:`itsdangerous` module. From ca176cb903f9566be07e359791e364ce2fe0b1bc Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 2 Jun 2017 06:36:13 -0700 Subject: [PATCH 265/399] pass serializer at tag init instead of to each method split tagged dict and passthrough into separate cases add docstrings --- flask/json/tag.py | 201 ++++++++++++++++++++++++++++++---------------- 1 file changed, 133 insertions(+), 68 deletions(-) diff --git a/flask/json/tag.py b/flask/json/tag.py index 40594282..3cf32a0c 100644 --- a/flask/json/tag.py +++ b/flask/json/tag.py @@ -10,179 +10,244 @@ from flask.json import dumps, loads class JSONTag(object): - __slots__ = () + """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" + + __slots__ = ('serializer',) key = None + """The tag to mark the serialized object with. If ``None``, this tag is + only used as an intermediate step during tagging.""" + + def __init__(self, serializer): + """Create a tagger for the given serializer.""" + + self.serializer = serializer + + def check(self, value): + """Check if the given value should be tagged by this tag.""" - def check(self, serializer, value): raise NotImplementedError - def to_json(self, serializer, value): + def to_json(self, value): + """Convert the Python object to an object that is a valid JSON type. + The tag will be added later.""" + raise NotImplementedError - def to_python(self, serializer, value): + def to_python(self, value): + """Convert the JSON representation back to the correct type. The tag + will already be removed.""" + raise NotImplementedError - def tag(self, serializer, value): - return {self.key: self.to_json(serializer, value)} + def tag(self, value): + """Convert the value to a valid JSON type and add the tag structure + around it.""" + + return {self.key: self.to_json(value)} class TagDict(JSONTag): - __slots__ = () - key = ' di' + """Tag for 1-item dicts whose only key matches a registered tag. - def check(self, serializer, value): - return isinstance(value, dict) + Internally, the dict key is suffixed with `__`, and the suffix is removed + when deserializing. + """ - def to_json(self, serializer, value, key=None): - if key is not None: - return {key + '__': serializer._tag(value[key])} + __slots__ = ('serializer',) + key = ' di' + + def check(self, value): + return ( + isinstance(value, dict) + and len(value) == 1 + and next(iter(value)) in self.serializer.tags + ) - return dict((k, serializer._tag(v)) for k, v in iteritems(value)) + def to_json(self, value): + key = next(iter(value)) + return {key + '__': self.serializer.tag(value[key])} - def to_python(self, serializer, value): + def to_python(self, value): key = next(iter(value)) return {key[:-2]: value[key]} - def tag(self, serializer, value): - if len(value) == 1: - key = next(iter(value)) - if key in serializer._tags: - return {self.key: self.to_json(serializer, value, key=key)} +class PassDict(JSONTag): + __slots__ = ('serializer',) + + def check(self, value): + return isinstance(value, dict) + + def to_json(self, value): + # JSON objects may only have string keys, so don't bother tagging the + # key here. + return dict((k, self.serializer.tag(v)) for k, v in iteritems(value)) - return self.to_json(serializer, value) + tag = to_json class TagTuple(JSONTag): - __slots__ = () + __slots__ = ('serializer',) key = ' t' - def check(self, serializer, value): + def check(self, value): return isinstance(value, tuple) - def to_json(self, serializer, value): - return [serializer._tag(item) for item in value] + def to_json(self, value): + return [self.serializer.tag(item) for item in value] - def to_python(self, serializer, value): + def to_python(self, value): return tuple(value) class PassList(JSONTag): - __slots__ = () + __slots__ = ('serializer',) - def check(self, serializer, value): + def check(self, value): return isinstance(value, list) - def to_json(self, serializer, value): - return [serializer._tag(item) for item in value] + def to_json(self, value): + return [self.serializer.tag(item) for item in value] tag = to_json class TagBytes(JSONTag): - __slots__ = () + __slots__ = ('serializer',) key = ' b' - def check(self, serializer, value): + def check(self, value): return isinstance(value, bytes) - def to_json(self, serializer, value): + def to_json(self, value): return b64encode(value).decode('ascii') - def to_python(self, serializer, value): + def to_python(self, value): return b64decode(value) class TagMarkup(JSONTag): - __slots__ = () + """Serialize anything matching the :class:`~markupsafe.Markup` API by + having a ``__html__`` method to the result of that method. Always + deserializes to an instance of :class:`~markupsafe.Markup`.""" + + __slots__ = ('serializer',) key = ' m' - def check(self, serializer, value): + def check(self, value): return callable(getattr(value, '__html__', None)) - def to_json(self, serializer, value): + def to_json(self, value): return text_type(value.__html__()) - def to_python(self, serializer, value): + def to_python(self, value): return Markup(value) class TagUUID(JSONTag): - __slots__ = () + __slots__ = ('serializer',) key = ' u' - def check(self, serializer, value): + def check(self, value): return isinstance(value, UUID) - def to_json(self, serializer, value): + def to_json(self, value): return value.hex - def to_python(self, serializer, value): + def to_python(self, value): return UUID(value) class TagDateTime(JSONTag): - __slots__ = () + __slots__ = ('serializer',) key = ' d' - def check(self, serializer, value): + def check(self, value): return isinstance(value, datetime) - def to_json(self, serializer, value): + def to_json(self, value): return http_date(value) - def to_python(self, serializer, value): + def to_python(self, value): return parse_date(value) class TaggedJSONSerializer(object): - __slots__ = ('_tags', '_order') - _default_tags = [ - TagDict(), TagTuple(), PassList(), TagBytes(), TagMarkup(), TagUUID(), - TagDateTime(), + """Serializer that uses a tag system to compactly represent objects that + are not JSON types. Passed as the intermediate serializer to + :class:`itsdangerous.Serializer`.""" + + __slots__ = ('tags', 'order') + default_tags = [ + TagDict, PassDict, TagTuple, PassList, TagBytes, TagMarkup, TagUUID, + TagDateTime, ] + """Tag classes to bind when creating the serializer. Other tags can be + added later using :meth:`~register`.""" def __init__(self): - self._tags = {} - self._order = [] + self.tags = {} + self.order = [] - for tag in self._default_tags: - self.register(tag) + for cls in self.default_tags: + self.register(cls) - def register(self, tag, force=False, index=-1): + def register(self, tag_class, force=False, index=-1): + """Register a new tag with this serializer. + + :param tag_class: tag class to register. Will be instantiated with this + serializer instance. + :param force: overwrite an existing tag. If false (default), a + :exc:`KeyError` is raised. + :param index: index to insert the new tag in the tag order. Useful when + the new tag is a special case of an existing tag. If -1 (default), + the tag is appended to the end of the order. + + :raise KeyError: if the tag key is already registered and ``force`` is + not true. + """ + + tag = tag_class(self) key = tag.key if key is not None: - if not force and key in self._tags: + if not force and key in self.tags: raise KeyError("Tag '{0}' is already registered.".format(key)) - self._tags[key] = tag + self.tags[key] = tag if index == -1: - self._order.append(tag) + self.order.append(tag) else: - self._order.insert(index, tag) + self.order.insert(index, tag) - def _tag(self, value): - for tag in self._order: - if tag.check(self, value): - return tag.tag(self, value) + def tag(self, value): + """Convert a value to a tagged representation if necessary.""" + + for tag in self.order: + if tag.check(value): + return tag.tag(value) return value - def _untag(self, value): + def untag(self, value): + """Convert a tagged representation back to the original type.""" + if len(value) != 1: return value key = next(iter(value)) - if key not in self._tags: + if key not in self.tags: return value - return self._tags[key].to_python(self, value[key]) + return self.tags[key].to_python(value[key]) def dumps(self, value): - return dumps(self._tag(value), separators=(',', ':')) + """Tag the value and dump it to a compact JSON string.""" + + return dumps(self.tag(value), separators=(',', ':')) def loads(self, value): - return loads(value, object_hook=self._untag) + """Load data from a JSON string and deserialized any tagged objects.""" + return loads(value, object_hook=self.untag) From bbd15d53ad129a2ef984fdf4dca1d93f2d913803 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 2 Jun 2017 06:49:55 -0700 Subject: [PATCH 266/399] docs style cleanup [ci skip] --- docs/api.rst | 27 +++++++++++++++------------ flask/json/tag.py | 35 +++++++++++++++++++---------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index b5009907..1b0da45a 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -171,18 +171,6 @@ implementation that Flask is using. .. autoclass:: SessionMixin :members: -.. autodata:: session_json_serializer - - This object provides dumping and loading methods similar to simplejson - but it also tags certain builtin Python objects that commonly appear in - sessions. Currently the following extended values are supported in - the JSON it dumps: - - - :class:`~markupsafe.Markup` objects - - :class:`~uuid.UUID` objects - - :class:`~datetime.datetime` objects - - :class:`tuple`\s - .. admonition:: Notice The ``PERMANENT_SESSION_LIFETIME`` config key can also be an integer @@ -354,6 +342,21 @@ you are using Flask 0.10 which implies that: .. autoclass:: JSONDecoder :members: +Tagged JSON +~~~~~~~~~~~ + +A compact representation for lossless serialization of non-standard JSON types. +:class:`~flask.sessions.SecureCookieSessionInterface` uses this to serialize +the session data, but it may be useful in other places. + +.. py:currentmodule:: flask.json.tag + +.. autoclass:: TaggedJSONSerializer + :members: + +.. autoclass:: JSONTag + :members: + Template Rendering ------------------ diff --git a/flask/json/tag.py b/flask/json/tag.py index 3cf32a0c..0ac583b5 100644 --- a/flask/json/tag.py +++ b/flask/json/tag.py @@ -13,36 +13,32 @@ class JSONTag(object): """Base class for defining type tags for :class:`TaggedJSONSerializer`.""" __slots__ = ('serializer',) + + #: The tag to mark the serialized object with. If ``None``, this tag is + #: only used as an intermediate step during tagging. key = None - """The tag to mark the serialized object with. If ``None``, this tag is - only used as an intermediate step during tagging.""" def __init__(self, serializer): """Create a tagger for the given serializer.""" - self.serializer = serializer def check(self, value): """Check if the given value should be tagged by this tag.""" - raise NotImplementedError def to_json(self, value): """Convert the Python object to an object that is a valid JSON type. The tag will be added later.""" - raise NotImplementedError def to_python(self, value): """Convert the JSON representation back to the correct type. The tag will already be removed.""" - raise NotImplementedError def tag(self, value): """Convert the value to a valid JSON type and add the tag structure around it.""" - return {self.key: self.to_json(value)} @@ -127,9 +123,9 @@ class TagBytes(JSONTag): class TagMarkup(JSONTag): - """Serialize anything matching the :class:`~markupsafe.Markup` API by + """Serialize anything matching the :class:`~flask.Markup` API by having a ``__html__`` method to the result of that method. Always - deserializes to an instance of :class:`~markupsafe.Markup`.""" + deserializes to an instance of :class:`~flask.Markup`.""" __slots__ = ('serializer',) key = ' m' @@ -175,15 +171,26 @@ class TagDateTime(JSONTag): class TaggedJSONSerializer(object): """Serializer that uses a tag system to compactly represent objects that are not JSON types. Passed as the intermediate serializer to - :class:`itsdangerous.Serializer`.""" + :class:`itsdangerous.Serializer`. + + The following extra types are supported: + + * :class:`dict` + * :class:`tuple` + * :class:`bytes` + * :class:`~flask.Markup` + * :class:`~uuid.UUID` + * :class:`~datetime.datetime` + """ __slots__ = ('tags', 'order') + + #: Tag classes to bind when creating the serializer. Other tags can be + #: added later using :meth:`~register`. default_tags = [ TagDict, PassDict, TagTuple, PassList, TagBytes, TagMarkup, TagUUID, TagDateTime, ] - """Tag classes to bind when creating the serializer. Other tags can be - added later using :meth:`~register`.""" def __init__(self): self.tags = {} @@ -206,7 +213,6 @@ class TaggedJSONSerializer(object): :raise KeyError: if the tag key is already registered and ``force`` is not true. """ - tag = tag_class(self) key = tag.key @@ -223,7 +229,6 @@ class TaggedJSONSerializer(object): def tag(self, value): """Convert a value to a tagged representation if necessary.""" - for tag in self.order: if tag.check(value): return tag.tag(value) @@ -232,7 +237,6 @@ class TaggedJSONSerializer(object): def untag(self, value): """Convert a tagged representation back to the original type.""" - if len(value) != 1: return value @@ -245,7 +249,6 @@ class TaggedJSONSerializer(object): def dumps(self, value): """Tag the value and dump it to a compact JSON string.""" - return dumps(self.tag(value), separators=(',', ':')) def loads(self, value): From 9bee2500dde2976befd7e88de5d403a26d7ad675 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 2 Jun 2017 08:09:37 -0700 Subject: [PATCH 267/399] finish documentation [ci skip] --- docs/api.rst | 15 +------------ flask/json/tag.py | 57 ++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 50 insertions(+), 22 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 1b0da45a..fe4f151f 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -342,20 +342,7 @@ you are using Flask 0.10 which implies that: .. autoclass:: JSONDecoder :members: -Tagged JSON -~~~~~~~~~~~ - -A compact representation for lossless serialization of non-standard JSON types. -:class:`~flask.sessions.SecureCookieSessionInterface` uses this to serialize -the session data, but it may be useful in other places. - -.. py:currentmodule:: flask.json.tag - -.. autoclass:: TaggedJSONSerializer - :members: - -.. autoclass:: JSONTag - :members: +.. automodule:: flask.json.tag Template Rendering ------------------ diff --git a/flask/json/tag.py b/flask/json/tag.py index 0ac583b5..3c57884e 100644 --- a/flask/json/tag.py +++ b/flask/json/tag.py @@ -1,3 +1,44 @@ +""" +Tagged JSON +~~~~~~~~~~~ + +A compact representation for lossless serialization of non-standard JSON types. +:class:`~flask.sessions.SecureCookieSessionInterface` uses this to serialize +the session data, but it may be useful in other places. It can be extended to +support other types. + +.. autoclass:: TaggedJSONSerializer + :members: + +.. autoclass:: JSONTag + :members: + +Let's seen an example that adds support for :class:`~collections.OrderedDict`. +Dicts don't have an order in Python or JSON, so to handle this we will dump +the items as a list of ``[key, value]`` pairs. Subclass :class:`JSONTag` and +give it the new key ``' od'`` to identify the type. The session serializer +processes dicts first, so insert the new tag at the front of the order since +``OrderedDict`` must be processed before ``dict``. :: + + from flask.json.tag import JSONTag + + class TagOrderedDict(JSONTag): + __slots__ = ('serializer',) + key = ' od' + + def check(self, value): + return isinstance(value, OrderedDict) + + def to_json(self, value): + return [[k, self.serializer.tag(v)] for k, v in iteritems(value)] + + def to_python(self, value): + return OrderedDict(value) + + app.session_interface.serializer.register(TagOrderedDict, 0) + +""" + from base64 import b64decode, b64encode from datetime import datetime from uuid import UUID @@ -49,7 +90,7 @@ class TagDict(JSONTag): when deserializing. """ - __slots__ = ('serializer',) + __slots__ = () key = ' di' def check(self, value): @@ -69,7 +110,7 @@ class TagDict(JSONTag): class PassDict(JSONTag): - __slots__ = ('serializer',) + __slots__ = () def check(self, value): return isinstance(value, dict) @@ -83,7 +124,7 @@ class PassDict(JSONTag): class TagTuple(JSONTag): - __slots__ = ('serializer',) + __slots__ = () key = ' t' def check(self, value): @@ -97,7 +138,7 @@ class TagTuple(JSONTag): class PassList(JSONTag): - __slots__ = ('serializer',) + __slots__ = () def check(self, value): return isinstance(value, list) @@ -109,7 +150,7 @@ class PassList(JSONTag): class TagBytes(JSONTag): - __slots__ = ('serializer',) + __slots__ = () key = ' b' def check(self, value): @@ -127,7 +168,7 @@ class TagMarkup(JSONTag): having a ``__html__`` method to the result of that method. Always deserializes to an instance of :class:`~flask.Markup`.""" - __slots__ = ('serializer',) + __slots__ = () key = ' m' def check(self, value): @@ -141,7 +182,7 @@ class TagMarkup(JSONTag): class TagUUID(JSONTag): - __slots__ = ('serializer',) + __slots__ = () key = ' u' def check(self, value): @@ -155,7 +196,7 @@ class TagUUID(JSONTag): class TagDateTime(JSONTag): - __slots__ = ('serializer',) + __slots__ = () key = ' d' def check(self, value): From daf85d3725516a17e474b143d6d67b95140db285 Mon Sep 17 00:00:00 2001 From: Grant Wu Date: Fri, 2 Jun 2017 12:00:54 -0400 Subject: [PATCH 268/399] Clarify documentation for json parsing Documentation does not currently mention that is_json accepts mimetypes that are not strictly application/json. --- flask/wrappers.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index 04bdcb5d..9a111828 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -97,7 +97,7 @@ class Request(RequestBase): @property def json(self): - """If the mimetype is :mimetype:`application/json` this will contain the + """If self.is_json would return true, this will contain the parsed JSON data. Otherwise this will be ``None``. The :meth:`get_json` method should be used instead. @@ -124,11 +124,10 @@ class Request(RequestBase): def get_json(self, force=False, silent=False, cache=True): """Parses the incoming JSON request data and returns it. By default - this function will return ``None`` if the mimetype is not - :mimetype:`application/json` but this can be overridden by the - ``force`` parameter. If parsing fails the - :meth:`on_json_loading_failed` method on the request object will be - invoked. + this function will return ``None`` if self.is_json would return false, + but this can be overridden by the ``force`` parameter. If parsing + fails, the :meth:`on_json_loading_failed` method on the request object + will be invoked. :param force: if set to ``True`` the mimetype is ignored. :param silent: if set to ``True`` this method will fail silently From fd8b95952c3891c402b18d1d35c6a2d263b8c6fb Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 2 Jun 2017 10:01:30 -0700 Subject: [PATCH 269/399] add tests for flask.json.tag --- tests/test_basic.py | 47 ++++++++++++++---------------- tests/test_json_tag.py | 65 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 25 deletions(-) create mode 100644 tests/test_json_tag.py diff --git a/tests/test_basic.py b/tests/test_basic.py index e4f68832..108c1409 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -435,34 +435,31 @@ def test_session_special_types(app, client): now = datetime.utcnow().replace(microsecond=0) the_uuid = uuid.uuid4() - @app.after_request - def modify_session(response): - flask.session['m'] = flask.Markup('Hello!') - flask.session['u'] = the_uuid - flask.session['dt'] = now - flask.session['b'] = b'\xff' - flask.session['t'] = (1, 2, 3) - flask.session['spacefirst'] = {' t': 'not-a-tuple'} - flask.session['withunderscores'] = {' t__': 'not-a-tuple'} - flask.session['notadict'] = {' di': 'not-a-dict'} - return response - @app.route('/') def dump_session_contents(): - return pickle.dumps(dict(flask.session)) + flask.session['t'] = (1, 2, 3) + flask.session['b'] = b'\xff' + flask.session['m'] = flask.Markup('') + flask.session['u'] = the_uuid + flask.session['d'] = now + flask.session['t_tag'] = {' t': 'not-a-tuple'} + flask.session['di_t_tag'] = {' t__': 'not-a-tuple'} + flask.session['di_tag'] = {' di': 'not-a-dict'} + return '', 204 - client.get('/') - rv = pickle.loads(client.get('/').data) - assert rv['m'] == flask.Markup('Hello!') - assert type(rv['m']) == flask.Markup - assert rv['dt'] == now - assert rv['u'] == the_uuid - assert rv['b'] == b'\xff' - assert type(rv['b']) == bytes - assert rv['t'] == (1, 2, 3) - assert rv['spacefirst'] == {' t': 'not-a-tuple'} - assert rv['withunderscores'] == {' t__': 'not-a-tuple'} - assert rv['notadict'] == {' di': 'not-a-dict'} + with client: + client.get('/') + s = flask.session + assert s['t'] == (1, 2, 3) + assert type(s['b']) == bytes + assert s['b'] == b'\xff' + assert type(s['m']) == flask.Markup + assert s['m'] == flask.Markup('') + assert s['u'] == the_uuid + assert s['d'] == now + assert s['t_tag'] == {' t': 'not-a-tuple'} + assert s['di_t_tag'] == {' t__': 'not-a-tuple'} + assert s['di_tag'] == {' di': 'not-a-dict'} def test_session_cookie_setting(app): diff --git a/tests/test_json_tag.py b/tests/test_json_tag.py new file mode 100644 index 00000000..b8cb6550 --- /dev/null +++ b/tests/test_json_tag.py @@ -0,0 +1,65 @@ +from datetime import datetime +from uuid import uuid4 + +import pytest + +from flask import Markup +from flask.json.tag import TaggedJSONSerializer, JSONTag + + +@pytest.mark.parametrize("data", ( + {' t': (1, 2, 3)}, + {' t__': b'a'}, + {' di': ' di'}, + {'x': (1, 2, 3), 'y': 4}, + (1, 2, 3), + [(1, 2, 3)], + b'\xff', + Markup(''), + uuid4(), + datetime.utcnow().replace(microsecond=0), +)) +def test_dump_load_unchanged(data): + s = TaggedJSONSerializer() + assert s.loads(s.dumps(data)) == data + + +def test_duplicate_tag(): + class TagDict(JSONTag): + key = ' d' + + s = TaggedJSONSerializer() + pytest.raises(KeyError, s.register, TagDict) + s.register(TagDict, force=True, index=0) + assert isinstance(s.tags[' d'], TagDict) + assert isinstance(s.order[0], TagDict) + + +def test_custom_tag(): + class Foo(object): + def __init__(self, data): + self.data = data + + class TagFoo(JSONTag): + __slots__ = () + key = ' f' + + def check(self, value): + return isinstance(value, Foo) + + def to_json(self, value): + return self.serializer.tag(value.data) + + def to_python(self, value): + return Foo(value) + + s = TaggedJSONSerializer() + s.register(TagFoo) + assert s.loads(s.dumps(Foo('bar'))).data == 'bar' + + +def test_tag_interface(): + t = JSONTag(None) + pytest.raises(NotImplementedError, t.check, None) + pytest.raises(NotImplementedError, t.to_json, None) + pytest.raises(NotImplementedError, t.to_python, None) From 217d5f9bc063ef8a605002648035c3e03c103a5f Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 2 Jun 2017 10:17:40 -0700 Subject: [PATCH 270/399] mention mimetype and is_json --- flask/wrappers.py | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/flask/wrappers.py b/flask/wrappers.py index 9a111828..5a49132e 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -97,8 +97,9 @@ class Request(RequestBase): @property def json(self): - """If self.is_json would return true, this will contain the - parsed JSON data. Otherwise this will be ``None``. + """If the request has a JSON mimetype like :mimetype:`application/json` + (see :meth:`is_json`), this will contain the parsed JSON data. + Otherwise this will be ``None``. The :meth:`get_json` method should be used instead. """ @@ -109,7 +110,7 @@ class Request(RequestBase): @property def is_json(self): - """Indicates if this request is JSON or not. By default a request + """Indicates if this request is JSON or not. By default a request is considered to include JSON data if the mimetype is :mimetype:`application/json` or :mimetype:`application/*+json`. @@ -123,17 +124,18 @@ class Request(RequestBase): return False def get_json(self, force=False, silent=False, cache=True): - """Parses the incoming JSON request data and returns it. By default - this function will return ``None`` if self.is_json would return false, - but this can be overridden by the ``force`` parameter. If parsing - fails, the :meth:`on_json_loading_failed` method on the request object - will be invoked. + """Parses the incoming JSON request data and returns it. By default + this function will return ``None`` if the request does not use a JSON + mimetype like :mimetype:`application/json`. See :meth:`is_json`. This + can be overridden by the ``force`` parameter. If parsing fails, + the :meth:`on_json_loading_failed` method on the request object will be + invoked. :param force: if set to ``True`` the mimetype is ignored. :param silent: if set to ``True`` this method will fail silently - and return ``None``. + and return ``None``. :param cache: if set to ``True`` the parsed JSON data is remembered - on the request. + on the request. """ rv = getattr(self, '_cached_json', _missing) # We return cached JSON only when the cache is enabled. From 5978a0f55f8ffd723d552645d70ac92e4e1148e4 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 2 Jun 2017 11:07:53 -0700 Subject: [PATCH 271/399] failing test for streaming session --- tests/test_helpers.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index a2d22dd6..c66e650b 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -861,6 +861,20 @@ class TestStreaming(object): assert rv.data == b'Hello World!' assert called == [42] + def test_stream_keeps_session(self, app, client): + @app.route('/') + def index(): + flask.session['test'] = 'flask' + + @flask.stream_with_context + def gen(): + yield flask.session['test'] + + return flask.Response(gen()) + + rv = client.get('/') + assert rv.data == b'flask' + class TestSafeJoin(object): def test_safe_join(self): From 6637e201743225f8fc840d3b7cfcdae67f2895ef Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 2 Jun 2017 11:10:18 -0700 Subject: [PATCH 272/399] only open session if request hasn't been pushed yet closes #1348 --- CHANGES | 4 ++++ flask/ctx.py | 19 +++++++++++-------- 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index a3504ad5..53d11697 100644 --- a/CHANGES +++ b/CHANGES @@ -67,6 +67,9 @@ Major release, unreleased of the generic bad request message. (`#2348`_) - Allow registering new tags with ``TaggedJSONSerializer`` to support storing other types in the session cookie. (`#2352`_) +- Only open the session if the request has not been pushed onto the context + stack yet. This allows ``stream_with_context`` generators to access the same + session that the containing view uses. (`#2354`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -87,6 +90,7 @@ Major release, unreleased .. _#2326: https://github.com/pallets/flask/pull/2326 .. _#2348: https://github.com/pallets/flask/pull/2348 .. _#2352: https://github.com/pallets/flask/pull/2352 +.. _#2354: https://github.com/pallets/flask/pull/2354 Version 0.12.2 -------------- diff --git a/flask/ctx.py b/flask/ctx.py index 70743de6..9e184c18 100644 --- a/flask/ctx.py +++ b/flask/ctx.py @@ -325,15 +325,18 @@ class RequestContext(object): _request_ctx_stack.push(self) - # Open the session at the moment that the request context is - # available. This allows a custom open_session method to use the - # request context (e.g. code that access database information - # stored on `g` instead of the appcontext). - session_interface = self.app.session_interface - self.session = session_interface.open_session(self.app, self.request) - + # Open the session at the moment that the request context is available. + # This allows a custom open_session method to use the request context. + # Only open a new session if this is the first time the request was + # pushed, otherwise stream_with_context loses the session. if self.session is None: - self.session = session_interface.make_null_session(self.app) + session_interface = self.app.session_interface + self.session = session_interface.open_session( + self.app, self.request + ) + + if self.session is None: + self.session = session_interface.make_null_session(self.app) def pop(self, exc=_sentinel): """Pops the request context and unbinds it by doing that. This will From e97253e4c1a0380f0b70108e8f984b0d9b87ac11 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 4 Jun 2017 11:44:00 -0700 Subject: [PATCH 273/399] clean up JSON code and docs --- CHANGES | 12 ++-- docs/testing.rst | 25 ++++----- flask/testing.py | 12 ++-- flask/wrappers.py | 125 ++++++++++++++++++++++-------------------- tests/test_testing.py | 2 +- 5 files changed, 92 insertions(+), 84 deletions(-) diff --git a/CHANGES b/CHANGES index 7e9f1f75..2c4ecdde 100644 --- a/CHANGES +++ b/CHANGES @@ -70,6 +70,12 @@ Major release, unreleased - Only open the session if the request has not been pushed onto the context stack yet. This allows ``stream_with_context`` generators to access the same session that the containing view uses. (`#2354`_) +- Add ``json`` keyword argument for the test client request methods. This will + dump the given object as JSON and set the appropriate content type. + (`#2358`_) +- Extract JSON handling to a mixin applied to both the request and response + classes used by Flask. This adds the ``is_json`` and ``get_json`` methods to + the response to make testing JSON response much easier. (`#2358`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -91,6 +97,7 @@ Major release, unreleased .. _#2348: https://github.com/pallets/flask/pull/2348 .. _#2352: https://github.com/pallets/flask/pull/2352 .. _#2354: https://github.com/pallets/flask/pull/2354 +.. _#2358: https://github.com/pallets/flask/pull/2358 Version 0.12.2 -------------- @@ -126,11 +133,6 @@ Released on December 21st 2016, codename Punsch. ``application/octet-stream``. See pull request ``#1988``. - Make ``flask.safe_join`` able to join multiple paths like ``os.path.join`` (pull request ``#1730``). -- Added `json` keyword argument to :meth:`flask.testing.FlaskClient.open` - (and related ``get``, ``post``, etc.), which makes it more convenient to - send JSON requests from the test client. -- Added ``is_json`` and ``get_json`` to :class:``flask.wrappers.Response`` - in order to make it easier to build assertions when testing JSON responses. - Revert a behavior change that made the dev server crash instead of returning a Internal Server Error (pull request ``#2006``). - Correctly invoke response handlers for both regular request dispatching as diff --git a/docs/testing.rst b/docs/testing.rst index 15d0d34e..a040b7ef 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -382,28 +382,27 @@ Testing JSON APIs .. versionadded:: 1.0 -Flask has great support for JSON, and is a popular choice for building REST -APIs. Testing both JSON requests and responses using the test client is very -convenient:: +Flask has great support for JSON, and is a popular choice for building JSON +APIs. Making requests with JSON data and examining JSON data in responses is +very convenient:: - from flask import jsonify + from flask import request, jsonify @app.route('/api/auth') def auth(): json_data = request.get_json() email = json_data['email'] password = json_data['password'] - return jsonify(token=generate_token(email, password)) with app.test_client() as c: - email = 'john@example.com' - password = 'secret' - resp = c.post('/api/auth', json={'login': email, 'password': password}) - - json_data = resp.get_json() + rv = c.post('/api/auth', json={ + 'username': 'flask', 'password': 'secret' + }) + json_data = rv.get_json() assert verify_token(email, json_data['token']) -Note that if the ``json`` argument is provided then the test client will put -JSON-serialized data in the request body, and also set the -``Content-Type: application/json`` HTTP header. +Passing the ``json`` argument in the test client methods sets the request data +to the JSON-serialized object and sets the content type to +``application/json``. You can get the JSON data from the request or response +with ``get_json``. diff --git a/flask/testing.py b/flask/testing.py index 54a4281e..f73454af 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -23,7 +23,7 @@ except ImportError: def make_test_environ_builder( - app, path='/', base_url=None, subdomain=None, url_scheme=None, json=None, + app, path='/', base_url=None, subdomain=None, url_scheme=None, *args, **kwargs ): """Creates a new test builder with some application defaults thrown in.""" @@ -54,12 +54,14 @@ def make_test_environ_builder( path += sep + url.query if 'json' in kwargs: - if 'data' in kwargs: - raise ValueError('Client cannot provide both `json` and `data`') + assert 'data' not in kwargs, ( + "Client cannot provide both 'json' and 'data'." + ) - kwargs['data'] = json_dumps(kwargs.pop('json')) + # push a context so flask.json can use app's json attributes + with app.app_context(): + kwargs['data'] = json_dumps(kwargs.pop('json')) - # Only set Content-Type when not explicitly provided if 'content_type' not in kwargs: kwargs['content_type'] = 'application/json' diff --git a/flask/wrappers.py b/flask/wrappers.py index cfb02272..918b0a93 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -8,111 +8,106 @@ :copyright: (c) 2015 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ +from warnings import warn -from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase from werkzeug.exceptions import BadRequest +from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase -from . import json -from .globals import _app_ctx_stack +from flask import json +from flask.globals import current_app class JSONMixin(object): """Common mixin for both request and response objects to provide JSON parsing capabilities. - .. versionadded:: 0.12 + .. versionadded:: 1.0 """ + _cached_json = Ellipsis + @property def is_json(self): - """Indicates if this request/response is in JSON format or not. By - default it is considered to include JSON data if the mimetype is + """Check if the mimetype indicates JSON data, either :mimetype:`application/json` or :mimetype:`application/*+json`. - .. versionadded:: 1.0 + .. versionadded:: 0.11 """ mt = self.mimetype - if mt == 'application/json': - return True - if mt.startswith('application/') and mt.endswith('+json'): - return True - return False + return ( + mt == 'application/json' + or (mt.startswith('application/')) and mt.endswith('+json') + ) @property def json(self): - """If this request/response is in JSON format then this property will - contain the parsed JSON data. Otherwise it will be ``None``. + """This will contain the parsed JSON data if the mimetype indicates + JSON (:mimetype:`application/json`, see :meth:`is_json`), otherwise it + will be ``None``. - The :meth:`get_json` method should be used instead. + .. deprecated:: 1.0 + Use :meth:`get_json` instead. """ - from warnings import warn warn(DeprecationWarning( - 'json is deprecated. Use get_json() instead.'), stacklevel=2) + "'json' is deprecated. Use 'get_json()' instead." + ), stacklevel=2) return self.get_json() def _get_data_for_json(self, cache): - getter = getattr(self, 'get_data', None) - if getter is not None: - return getter(cache=cache) - return self.data + return self.get_data(cache=cache) def get_json(self, force=False, silent=False, cache=True): - """Parses the JSON request/response data and returns it. By default - this function will return ``None`` if the mimetype is not - :mimetype:`application/json` but this can be overridden by the - ``force`` parameter. If parsing fails the - :meth:`on_json_loading_failed` method on the request object will be - invoked. - - :param force: if set to ``True`` the mimetype is ignored. - :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 object. + """Parse and return the data as JSON. If the mimetype does not indicate + JSON (:mimetype:`application/json`, see :meth:`is_json`), this returns + ``None`` unless ``force`` is true. If parsing fails, + :meth:`on_json_loading_failed` is called and its return value is used + as the return value. + + :param force: Ignore the mimetype and always try to parse JSON. + :param silent: Silence parsing errors and return ``None`` instead. + :param cache: Store the parsed JSON to return for subsequent calls. """ - try: - return getattr(self, '_cached_json') - except AttributeError: - pass + if cache and self._cached_json is not Ellipsis: + return self._cached_json if not (force or self.is_json): return None - # We accept MIME charset header against the specification as certain - # clients have been using this in the past. For responses, we assume - # that if the response charset was set explicitly then the data had - # been encoded correctly as well. + # We accept MIME charset against the specification as certain clients + # have used this in the past. For responses, we assume that if the + # charset is set then the data has been encoded correctly as well. charset = self.mimetype_params.get('charset') + try: - data = self._get_data_for_json(cache) - if charset is not None: - rv = json.loads(data, encoding=charset) - else: - rv = json.loads(data) + data = self._get_data_for_json(cache=cache) + rv = json.loads(data, encoding=charset) except ValueError as e: if silent: rv = None else: rv = self.on_json_loading_failed(e) + if cache: self._cached_json = rv + return rv def on_json_loading_failed(self, e): - """Called if decoding of the JSON data failed. The return value of - this method is used by :meth:`get_json` when an error occurred. The - default implementation just raises a :class:`BadRequest` exception. + """Called if :meth:`get_json` parsing fails and isn't silenced. If + this method returns a value, it is used as the return value for + :meth:`get_json`. The default implementation raises a + :class:`BadRequest` exception. .. versionchanged:: 0.10 - Removed buggy previous behavior of generating a random JSON - response. If you want that behavior back you can trivially - add it by subclassing. + Raise a :exc:`BadRequest` error instead of returning an error + message as JSON. If you want that behavior you can add it by + subclassing. .. versionadded:: 0.8 """ - ctx = _app_ctx_stack.top - if ctx is not None and ctx.app.debug: + if current_app is not None and current_app.debug: raise BadRequest('Failed to decode JSON object: {0}'.format(e)) + raise BadRequest() @@ -153,9 +148,8 @@ class Request(RequestBase, JSONMixin): @property def max_content_length(self): """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" - ctx = _app_ctx_stack.top - if ctx is not None: - return ctx.app.config['MAX_CONTENT_LENGTH'] + if current_app: + return current_app.config['MAX_CONTENT_LENGTH'] @property def endpoint(self): @@ -191,9 +185,12 @@ class Request(RequestBase, JSONMixin): # In debug mode we're replacing the files multidict with an ad-hoc # subclass that raises a different error for key errors. - ctx = _app_ctx_stack.top - if ctx is not None and ctx.app.debug and \ - self.mimetype != 'multipart/form-data' and not self.files: + if ( + current_app + and current_app.debug + and self.mimetype != 'multipart/form-data' + and not self.files + ): from .debughelpers import attach_enctype_error_multidict attach_enctype_error_multidict(self) @@ -206,5 +203,13 @@ class Response(ResponseBase, JSONMixin): If you want to replace the response object used you can subclass this and set :attr:`~flask.Flask.response_class` to your subclass. + + .. versionchanged:: 1.0 + JSON support is added to the response, like the request. This is useful + when testing to get the test client response data as JSON. """ + default_mimetype = 'text/html' + + def _get_data_for_json(self, cache): + return self.get_data() diff --git a/tests/test_testing.py b/tests/test_testing.py index b742f2b8..251f5fee 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -267,7 +267,7 @@ def test_full_url_request(app, client): def test_json_request_and_response(app, client): @app.route('/echo', methods=['POST']) def echo(): - return jsonify(flask.request.json) + return jsonify(flask.request.get_json()) with client: json_data = {'drink': {'gin': 1, 'tonic': True}, 'price': 10} From fa7e8d6073052adc5ba69702db4dec6571ef0bfd Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 4 Jun 2017 12:26:21 -0700 Subject: [PATCH 274/399] be smarter about adding ".cli" to reloader command python -m flask.cli raises an import warning on > 2.6 it's only needed on 2.6, "flask" works otherwise closes #2357 --- flask/cli.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 346f0bf8..420491da 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -22,7 +22,7 @@ from threading import Lock, Thread import click from . import __version__ -from ._compat import iteritems, reraise +from ._compat import iteritems, reraise, PY2 from .globals import current_app from .helpers import get_debug_flag from ._compat import getargspec @@ -647,19 +647,19 @@ Example usage: 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 = 'flask' + + if sys.version_info < (2, 7): + this_module += '.cli' + + 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:] + # Python rewrites "python -m flask" to the path to the file in argv. + # Restore the original command so that the reloader works. + sys.argv = ['-m', this_module] + args else: name = None From 12c45f06a5e0c2303977ce91abc3614b4b00556f Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 4 Jun 2017 12:38:10 -0700 Subject: [PATCH 275/399] remove unused import --- flask/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index 420491da..0982aa1c 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -22,7 +22,7 @@ from threading import Lock, Thread import click from . import __version__ -from ._compat import iteritems, reraise, PY2 +from ._compat import iteritems, reraise from .globals import current_app from .helpers import get_debug_flag from ._compat import getargspec From 16396248b829c61806ba65840aa70e0a3d8b3c8c Mon Sep 17 00:00:00 2001 From: Grey Li Date: Mon, 5 Jun 2017 16:11:16 +0800 Subject: [PATCH 276/399] Include flask.json package in setup() --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 24cd9d83..a829dd8a 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ setup( description='A microframework based on Werkzeug, Jinja2 ' 'and good intentions', long_description=__doc__, - packages=['flask', 'flask.ext'], + packages=['flask', 'flask.ext', 'flask.json'], include_package_data=True, zip_safe=False, platforms='any', From 5c12acefbb90d4328f896633b636c8e05d87a0dd Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 5 Jun 2017 06:14:13 -0700 Subject: [PATCH 277/399] failing test --- tests/test_basic.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/tests/test_basic.py b/tests/test_basic.py index 108c1409..d24678c2 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -980,6 +980,39 @@ def test_http_error_subclass_handling(app, client): assert client.get('/3').data == b'apple' +def test_errorhandler_precedence(app, client): + class E1(Exception): + pass + + class E2(Exception): + pass + + class E3(E1, E2): + pass + + @app.errorhandler(E2) + def handle_e2(e): + return 'E2' + + @app.errorhandler(Exception) + def handle_exception(e): + return 'Exception' + + @app.route('/E1') + def raise_e1(): + raise E1 + + @app.route('/E3') + def raise_e3(): + raise E3 + + rv = client.get('/E1') + assert rv.data == b'Exception' + + rv = client.get('/E3') + assert rv.data == b'E2' + + def test_trapping_of_bad_request_key_errors(app, client): @app.route('/fail') def fail(): From b5f4c5215022a01823675038bc246dd1d771a543 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 5 Jun 2017 06:24:08 -0700 Subject: [PATCH 278/399] don't cache error handlers for exception mro closes #2267, closes #1433 --- CHANGES | 4 ++++ flask/app.py | 26 +++++++++++--------------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/CHANGES b/CHANGES index 2c4ecdde..59e8c4c1 100644 --- a/CHANGES +++ b/CHANGES @@ -76,6 +76,9 @@ Major release, unreleased - Extract JSON handling to a mixin applied to both the request and response classes used by Flask. This adds the ``is_json`` and ``get_json`` methods to the response to make testing JSON response much easier. (`#2358`_) +- Removed error handler caching because it caused unexpected results for some + exception inheritance hierarchies. Register handlers explicitly for each + exception if you don't want to traverse the MRO. (`#2362`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -98,6 +101,7 @@ Major release, unreleased .. _#2352: https://github.com/pallets/flask/pull/2352 .. _#2354: https://github.com/pallets/flask/pull/2354 .. _#2358: https://github.com/pallets/flask/pull/2358 +.. _#2362: https://github.com/pallets/flask/pull/2362 Version 0.12.2 -------------- diff --git a/flask/app.py b/flask/app.py index 342dde86..7034b755 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1484,32 +1484,28 @@ class Flask(_PackageBoundObject): return f def _find_error_handler(self, e): - """Find a registered error handler for a request in this order: + """Return a registered error handler for an exception in this order: blueprint handler for a specific code, app handler for a specific code, - blueprint generic HTTPException handler, app generic HTTPException handler, - and returns None if a suitable handler is not found. + blueprint handler for an exception class, app handler for an exception + class, or ``None`` if a suitable handler is not found. """ exc_class, code = self._get_exc_class_and_code(type(e)) - def find_handler(handler_map): + for name, c in ( + (request.blueprint, code), (None, code), + (request.blueprint, None), (None, None) + ): + handler_map = self.error_handler_spec.setdefault(name, {}).get(c) + if not handler_map: - return + continue for cls in exc_class.__mro__: handler = handler_map.get(cls) + if handler is not None: - # cache for next time exc_class is raised - handler_map[exc_class] = handler return handler - # check for any in blueprint or app - for name, c in ((request.blueprint, code), (None, code), - (request.blueprint, None), (None, None)): - handler = find_handler(self.error_handler_spec.get(name, {}).get(c)) - - if handler: - return handler - def handle_http_exception(self, e): """Handles an HTTP exception. By default this will invoke the registered error handlers and fall back to returning the From 235c830759beca55a9d141c689f2a8c88a19f3d2 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 6 Jun 2017 07:47:07 -0700 Subject: [PATCH 279/399] document inherited attributes for Flask and Blueprint closes #480 [ci skip] --- flask/app.py | 15 +++++++++++++++ flask/blueprints.py | 15 +++++++++++++++ flask/helpers.py | 39 ++++++++++++++++++++++++++------------- 3 files changed, 56 insertions(+), 13 deletions(-) diff --git a/flask/app.py b/flask/app.py index 7034b755..d80514bf 100644 --- a/flask/app.py +++ b/flask/app.py @@ -346,6 +346,21 @@ class Flask(_PackageBoundObject): #: .. versionadded:: 0.8 session_interface = SecureCookieSessionInterface() + # TODO remove the next three attrs when Sphinx :inherited-members: works + # https://github.com/sphinx-doc/sphinx/issues/741 + + #: The name of the package or module that this app belongs to. Do not + #: change this once it is set by the constructor. + import_name = None + + #: Location of the template files to be added to the template lookup. + #: ``None`` if templates should not be added. + template_folder = None + + #: Absolute path to the package on the filesystem. Used to look up + #: resources contained in the package. + root_path = None + def __init__(self, import_name, static_path=None, static_url_path=None, static_folder='static', static_host=None, host_matching=False, template_folder='templates', diff --git a/flask/blueprints.py b/flask/blueprints.py index 57d77512..ed51094e 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -96,6 +96,21 @@ class Blueprint(_PackageBoundObject): #: Set to ``None`` to use the app's :class:`~flask.app.Flask.json_decoder`. json_decoder = None + # TODO remove the next three attrs when Sphinx :inherited-members: works + # https://github.com/sphinx-doc/sphinx/issues/741 + + #: The name of the package or module that this app belongs to. Do not + #: change this once it is set by the constructor. + import_name = None + + #: Location of the template files to be added to the template lookup. + #: ``None`` if templates should not be added. + template_folder = None + + #: Absolute path to the package on the filesystem. Used to look up + #: resources contained in the package. + root_path = None + def __init__(self, name, import_name, static_folder=None, static_url_path=None, template_folder=None, url_prefix=None, subdomain=None, url_defaults=None, diff --git a/flask/helpers.py b/flask/helpers.py index f37be677..94c5ce77 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -480,10 +480,10 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, .. versionchanged:: 0.12 The `attachment_filename` is preferred over `filename` for MIME-type detection. - + .. versionchanged:: 0.13 UTF-8 filenames, as specified in `RFC 2231`_, are supported. - + .. _RFC 2231: https://tools.ietf.org/html/rfc2231#section-4 :param filename_or_fp: the filename of the file to send. @@ -848,43 +848,56 @@ class locked_cached_property(object): class _PackageBoundObject(object): + #: The name of the package or module that this app belongs to. Do not + #: change this once it is set by the constructor. + import_name = None + + #: Location of the template files to be added to the template lookup. + #: ``None`` if templates should not be added. + template_folder = None + + #: Absolute path to the package on the filesystem. Used to look up + #: resources contained in the package. + root_path = 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 - - #: location of the templates. ``None`` if templates should not be - #: 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 = root_path - self._static_folder = None self._static_url_path = None def _get_static_folder(self): if self._static_folder is not None: return os.path.join(self.root_path, self._static_folder) + def _set_static_folder(self, value): self._static_folder = value - static_folder = property(_get_static_folder, _set_static_folder, doc=''' - The absolute path to the configured static folder. - ''') + + static_folder = property( + _get_static_folder, _set_static_folder, + doc='The absolute path to the configured static folder.' + ) del _get_static_folder, _set_static_folder def _get_static_url_path(self): if self._static_url_path is not None: return self._static_url_path + if self.static_folder is not None: return '/' + os.path.basename(self.static_folder) + def _set_static_url_path(self, value): self._static_url_path = value - static_url_path = property(_get_static_url_path, _set_static_url_path) + + static_url_path = property( + _get_static_url_path, _set_static_url_path, + doc='The URL prefix that the static route will be registered for.' + ) del _get_static_url_path, _set_static_url_path @property From 15756da1f02fb997bdcc77f502a471e5466e56a2 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 6 Jun 2017 09:21:44 -0700 Subject: [PATCH 280/399] mention csp for javascript: uri xss closes #2174 [ci skip] --- docs/security.rst | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/docs/security.rst b/docs/security.rst index 0d4cfdeb..b8714186 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -38,7 +38,7 @@ either double or single quotes when using Jinja expressions in them: .. sourcecode:: html+jinja - the text + Why is this necessary? Because if you would not be doing that, an attacker could easily inject custom JavaScript handlers. For example an @@ -46,15 +46,26 @@ attacker could inject this piece of HTML+JavaScript: .. sourcecode:: html - onmouseover=alert(document.cookie) + onmouseover=alert(document.cookie) -When the user would then move with the mouse over the link, the cookie +When the user would then move with the mouse over the input, the cookie would be presented to the user in an alert window. But instead of showing the cookie to the user, a good attacker might also execute any other JavaScript code. In combination with CSS injections the attacker might even make the element fill out the entire page so that the user would just have to have the mouse anywhere on the page to trigger the attack. +There is one class of XSS issues that Jinja's escaping does not protect +against. The ``a`` tag's ``href`` attribute can contain a `javascript:` URI, +which the browser will execute when clicked if not secured properly. + +.. sourcecode:: html + + click here + click here + +To prevent this, you'll need to set the :ref:`security-csp` response header. + Cross-Site Request Forgery (CSRF) --------------------------------- @@ -125,6 +136,8 @@ man-in-the-middle (MITM) attacks. :: - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security +.. _security-csp: + Content Security Policy (CSP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -132,7 +145,7 @@ Tell the browser where it can load various types of resource from. This header should be used whenever possible, but requires some work to define the correct policy for your site. A very strict policy would be:: - response.headers['Content-Security-Policy'] = "default-src: 'self'" + response.headers['Content-Security-Policy'] = "default-src 'self'" - https://csp.withgoogle.com/docs/index.html - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy From 067686b471cec18a886240c33408a118449776de Mon Sep 17 00:00:00 2001 From: tristan fisher Date: Fri, 9 Jun 2017 10:19:08 -0400 Subject: [PATCH 281/399] Add basic how-to on using envvars for config --- docs/config.rst | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/docs/config.rst b/docs/config.rst index 1da7d2ee..096d527f 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -375,6 +375,54 @@ methods on the config object as well to load from individual files. For a complete reference, read the :class:`~flask.Config` object's documentation. +Configuring from Environmental Variables +---------------------------------------- + +In addition to pointing to configuration files using environment variables, you +may find it useful (or necessary) to control your configuration values +directly from the environment. + +Environment variables can be set on Linux or OS X with the export command in the +shell before starting the server:: + + $ export SECRET_KEY='?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' + $ export DEBUG=False + $ python run-app.py + * Running on http://127.0.0.1:5000/ + * Restarting with reloader... + +On Windows systems use the `set` builtin instead:: + + >set SECRET_KEY='?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' + >set DEBUG=False + +While this approach is straightforward to use, it is important to remember that +environment variables are strings -- they are not automatically deserialized into Python +types. + +Here is an example of a configuration file that uses environmental variables:: + + # Example configuration + import os + + ENVIRONMENT_DEBUG = os.environ.get("DEBUG", default=False) + if ENVIRONMENT_DEBUG.lower() in ("f", "false"): + ENVIRONMENT_DEBUG = False + + DEBUG = ENVIRONMENT_DEBUG + SECRET_KEY = os.environ.get("SECRET_KEY", default=None) + if not SECRET_KEY: + raise ValueError("No secret key set for Flask application") + + +Notice that any value besides a empty string will be interpreted as a boolean True value in Python, which +requires care if an environment may explicit setting values intended to be False. + +Make sure to load the configuration very early on, so that extensions have +the ability to access the configuration when starting up. There are other +methods on the config object as well to load from individual files. For a +complete reference, read the :class:`~flask.Config` object's +documentation. Configuration Best Practices ---------------------------- From 53ff054fd306bd012dac43b413ab9c0bf445bdfa Mon Sep 17 00:00:00 2001 From: tristan fisher Date: Fri, 9 Jun 2017 10:54:47 -0400 Subject: [PATCH 282/399] Fixes grammar and phrasing on envvar how-to --- docs/config.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 096d527f..e0541511 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -415,8 +415,8 @@ Here is an example of a configuration file that uses environmental variables:: raise ValueError("No secret key set for Flask application") -Notice that any value besides a empty string will be interpreted as a boolean True value in Python, which -requires care if an environment may explicit setting values intended to be False. +Notice that any value besides an empty string will be interpreted as a boolean True value in Python, which +requires care if an environment explicitly sets values intended to be False. Make sure to load the configuration very early on, so that extensions have the ability to access the configuration when starting up. There are other From 01d2f725d3fd243951607712fa67a6f3ec45dd8e Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 9 Jun 2017 10:28:54 -0700 Subject: [PATCH 283/399] formatting [ci skip] --- docs/config.rst | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index e0541511..186a3695 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -375,15 +375,15 @@ methods on the config object as well to load from individual files. For a complete reference, read the :class:`~flask.Config` object's documentation. -Configuring from Environmental Variables ----------------------------------------- +Configuring from Environment Variables +-------------------------------------- In addition to pointing to configuration files using environment variables, you -may find it useful (or necessary) to control your configuration values -directly from the environment. +may find it useful (or necessary) to control your configuration values directly +from the environment. -Environment variables can be set on Linux or OS X with the export command in the -shell before starting the server:: +Environment variables can be set on Linux or OS X with the export command in +the shell before starting the server:: $ export SECRET_KEY='?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' $ export DEBUG=False @@ -397,10 +397,10 @@ On Windows systems use the `set` builtin instead:: >set DEBUG=False While this approach is straightforward to use, it is important to remember that -environment variables are strings -- they are not automatically deserialized into Python -types. +environment variables are strings -- they are not automatically deserialized +into Python types. -Here is an example of a configuration file that uses environmental variables:: +Here is an example of a configuration file that uses environment variables:: # Example configuration import os @@ -415,14 +415,14 @@ Here is an example of a configuration file that uses environmental variables:: raise ValueError("No secret key set for Flask application") -Notice that any value besides an empty string will be interpreted as a boolean True value in Python, which -requires care if an environment explicitly sets values intended to be False. +Notice that any value besides an empty string will be interpreted as a boolean +``True`` value in Python, which requires care if an environment explicitly sets +values intended to be ``False``. -Make sure to load the configuration very early on, so that extensions have -the ability to access the configuration when starting up. There are other -methods on the config object as well to load from individual files. For a -complete reference, read the :class:`~flask.Config` object's -documentation. +Make sure to load the configuration very early on, so that extensions have the +ability to access the configuration when starting up. There are other methods +on the config object as well to load from individual files. For a complete +reference, read the :class:`~flask.Config` class documentation. Configuration Best Practices ---------------------------- From cf425403c8ff24623812a46272dc91f712ed5086 Mon Sep 17 00:00:00 2001 From: grahamlutz Date: Sat, 10 Jun 2017 08:53:40 -0400 Subject: [PATCH 284/399] Remove duplicate word from docstring --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index d80514bf..a6e4f18f 100644 --- a/flask/app.py +++ b/flask/app.py @@ -136,7 +136,7 @@ class Flask(_PackageBoundObject): at `static_url_path`. Defaults to the ``'static'`` folder in the root path of the application. :param host_matching: sets the app's ``url_map.host_matching`` to the given - given value. Defaults to False. + value. Defaults to False. :param static_host: the host to use when adding the static route. Defaults to None. Required when using ``host_matching=True`` with a ``static_folder`` configured. From 66e9dc9df26c8d86e20dd7232eb844facb3caea1 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Mon, 12 Jun 2017 16:07:24 -0500 Subject: [PATCH 285/399] Add docutils label for templates page --- docs/templating.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/templating.rst b/docs/templating.rst index 9ba2223a..c0af6639 100644 --- a/docs/templating.rst +++ b/docs/templating.rst @@ -1,3 +1,5 @@ +.. _templates: + Templates ========= From 91fd33dbd9fa1c886284df8462f459948fdb1920 Mon Sep 17 00:00:00 2001 From: Tony Narlock Date: Mon, 12 Jun 2017 16:15:52 -0500 Subject: [PATCH 286/399] Add docutils label for class-based views --- docs/api.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/api.rst b/docs/api.rst index a07fe8ca..421f3de5 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -639,6 +639,7 @@ The following signals exist in Flask: .. _blinker: https://pypi.python.org/pypi/blinker +.. _class-based-views: Class-Based Views ----------------- From 0f7b3a4f26dfbbe453a504abd4a8bcbf49194d46 Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 14 Jun 2017 07:16:55 -0700 Subject: [PATCH 287/399] document `Flask.register_blueprint` arguments closes #1809 --- flask/app.py | 33 ++++++++++++++++++++++++++------- flask/blueprints.py | 23 +++++++++++++++-------- 2 files changed, 41 insertions(+), 15 deletions(-) diff --git a/flask/app.py b/flask/app.py index a6e4f18f..7ac59fdb 100644 --- a/flask/app.py +++ b/flask/app.py @@ -994,22 +994,41 @@ class Flask(_PackageBoundObject): return self.session_interface.make_null_session(self) @setupmethod - def register_blueprint(self, blueprint, **options): - """Registers a blueprint on the application. + def register_blueprint( + self, blueprint, **options + ): + """Register a :class:`~flask.Blueprint` on the application. Keyword + arguments passed to this method will override the defaults set on the + blueprint. + + Calls the blueprint's :meth:`~flask.Blueprint.register` method after + recording the blueprint in the application's :attr:`blueprints`. + + :param url_prefix: Blueprint routes will be prefixed with this. + :param subdomain: Blueprint routes will match on this subdomain. + :param url_defaults: Blueprint routes will use these default values for + view arguments. + :param options: Additional keyword arguments are passed to + :class:`~flask.blueprints.BlueprintSetupState`. They can be + accessed in :meth:`~flask.Blueprint.record` callbacks. .. versionadded:: 0.7 """ first_registration = False + if blueprint.name in self.blueprints: - assert self.blueprints[blueprint.name] is blueprint, \ - 'A blueprint\'s name collision occurred between %r and ' \ - '%r. Both share the same name "%s". Blueprints that ' \ - 'are created on the fly need unique names.' % \ - (blueprint, self.blueprints[blueprint.name], blueprint.name) + assert self.blueprints[blueprint.name] is blueprint, ( + 'A name collision occurred between blueprints %r and %r. Both' + ' share the same name "%s". Blueprints that are created on the' + ' fly need unique names.' % ( + blueprint, self.blueprints[blueprint.name], blueprint.name + ) + ) else: self.blueprints[blueprint.name] = blueprint self._blueprint_order.append(blueprint) first_registration = True + blueprint.register(self, options, first_registration) def iter_blueprints(self): diff --git a/flask/blueprints.py b/flask/blueprints.py index ed51094e..80668dbe 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -159,18 +159,25 @@ class Blueprint(_PackageBoundObject): return BlueprintSetupState(self, app, options, first_registration) def register(self, app, options, first_registration=False): - """Called by :meth:`Flask.register_blueprint` to register a blueprint - on the application. This can be overridden to customize the register - behavior. Keyword arguments from - :func:`~flask.Flask.register_blueprint` are directly forwarded to this - method in the `options` dictionary. + """Called by :meth:`Flask.register_blueprint` to register all views + and callbacks registered on the blueprint with the application. Creates + a :class:`.BlueprintSetupState` and calls each :meth:`record` callback + with it. + + :param app: The application this blueprint is being registered with. + :param options: Keyword arguments forwarded from + :meth:`~Flask.register_blueprint`. + :param first_registration: Whether this is the first time this + blueprint has been registered on the application. """ self._got_registered_once = True state = self.make_setup_state(app, options, first_registration) + if self.has_static_folder: - state.add_url_rule(self.static_url_path + '/', - view_func=self.send_static_file, - endpoint='static') + state.add_url_rule( + self.static_url_path + '/', + view_func=self.send_static_file, endpoint='static' + ) for deferred in self.deferred_functions: deferred(state) From fe1bf3c8213c56e045ead8afa84773f181b3dab6 Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 14 Jun 2017 07:26:48 -0700 Subject: [PATCH 288/399] document the blueprint param too --- flask/app.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/flask/app.py b/flask/app.py index 7ac59fdb..ece1e2bf 100644 --- a/flask/app.py +++ b/flask/app.py @@ -994,9 +994,7 @@ class Flask(_PackageBoundObject): return self.session_interface.make_null_session(self) @setupmethod - def register_blueprint( - self, blueprint, **options - ): + def register_blueprint(self, blueprint, **options): """Register a :class:`~flask.Blueprint` on the application. Keyword arguments passed to this method will override the defaults set on the blueprint. @@ -1004,6 +1002,7 @@ class Flask(_PackageBoundObject): Calls the blueprint's :meth:`~flask.Blueprint.register` method after recording the blueprint in the application's :attr:`blueprints`. + :param blueprint: The blueprint to register. :param url_prefix: Blueprint routes will be prefixed with this. :param subdomain: Blueprint routes will match on this subdomain. :param url_defaults: Blueprint routes will use these default values for From 4fc48200a58907ffdf136961b84ef7d19192e6ef Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 14 Jun 2017 12:08:25 -0700 Subject: [PATCH 289/399] reconfigure the app from run command and method extract templates_auto_reload to property continues #1910 closes #1907 --- CHANGES | 3 +++ flask/app.py | 59 ++++++++++++++++++++++++++++++++++++++++++++-------- flask/cli.py | 8 ++++++- 3 files changed, 60 insertions(+), 10 deletions(-) diff --git a/CHANGES b/CHANGES index 59e8c4c1..7f9e968b 100644 --- a/CHANGES +++ b/CHANGES @@ -79,6 +79,8 @@ Major release, unreleased - Removed error handler caching because it caused unexpected results for some exception inheritance hierarchies. Register handlers explicitly for each exception if you don't want to traverse the MRO. (`#2362`_) +- Template auto reloading will honor the ``run`` command's ``debug`` flag even + if ``app.jinja_env`` was already accessed. (`#2373`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -102,6 +104,7 @@ Major release, unreleased .. _#2354: https://github.com/pallets/flask/pull/2354 .. _#2358: https://github.com/pallets/flask/pull/2358 .. _#2362: https://github.com/pallets/flask/pull/2362 +.. _#2373: https://github.com/pallets/flask/pull/2373 Version 0.12.2 -------------- diff --git a/flask/app.py b/flask/app.py index 7f487fc8..04c39547 100644 --- a/flask/app.py +++ b/flask/app.py @@ -703,6 +703,28 @@ class Flask(_PackageBoundObject): """ return open(os.path.join(self.instance_path, resource), mode) + def _get_templates_auto_reload(self): + """Reload templates when they are changed. Used by + :meth:`create_jinja_environment`. + + This attribute can be configured with :data:`TEMPLATES_AUTO_RELOAD`. If + not set, it will be enabled in debug mode. + + .. versionadded:: 1.0 + This property was added but the underlying config and behavior + already existed. + """ + rv = self.config['TEMPLATES_AUTO_RELOAD'] + return rv if rv is not None else self.debug + + def _set_templates_auto_reload(self, value): + self.config['TEMPLATES_AUTO_RELOAD'] = value + + templates_auto_reload = property( + _get_templates_auto_reload, _set_templates_auto_reload + ) + del _get_templates_auto_reload, _set_templates_auto_reload + def create_jinja_environment(self): """Creates the Jinja2 environment based on :attr:`jinja_options` and :meth:`select_jinja_autoescape`. Since 0.7 this also adds @@ -715,13 +737,13 @@ class Flask(_PackageBoundObject): ``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: - if self.config['TEMPLATES_AUTO_RELOAD'] is not None: - options['auto_reload'] = self.config['TEMPLATES_AUTO_RELOAD'] - else: - options['auto_reload'] = self.debug + options['auto_reload'] = self.templates_auto_reload + rv = self.jinja_environment(self, **options) rv.globals.update( url_for=url_for, @@ -806,6 +828,22 @@ class Flask(_PackageBoundObject): rv.update(processor()) return rv + def _reconfigure_for_run_debug(self, debug): + """The ``run`` commands will set the application's debug flag. Some + application configuration may already be calculated based on the + previous debug value. This method will recalculate affected values. + + Called by the :func:`flask.cli.run` command or :meth:`Flask.run` + method if the debug flag is set explicitly in the call. + + :param debug: the new value of the debug flag + + .. versionadded:: 1.0 + Reconfigures ``app.jinja_env.auto_reload``. + """ + self.debug = debug + self.jinja_env.auto_reload = self.templates_auto_reload + def run(self, host=None, port=None, debug=None, **options): """Runs the application on a local development server. @@ -859,21 +897,24 @@ class Flask(_PackageBoundObject): explain_ignored_app_run() return - from werkzeug.serving import run_simple + if debug is not None: + self._reconfigure_for_run_debug(bool(debug)) + _host = '127.0.0.1' _port = 5000 server_name = self.config.get("SERVER_NAME") sn_host, sn_port = None, None + if server_name: sn_host, _, sn_port = server_name.partition(':') + host = host or sn_host or _host port = int(port or sn_port or _port) - if debug is not None: - self.debug = bool(debug) options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) - if debug: - self.jinja_env.auto_reload = True + + from werkzeug.serving import run_simple + try: run_simple(host, port, self, **options) finally: diff --git a/flask/cli.py b/flask/cli.py index 0982aa1c..4b5323c7 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -319,8 +319,10 @@ class ScriptInfo(object): be returned. """ __traceback_hide__ = True + if self._loaded_app is not None: return self._loaded_app + if self.create_app is not None: rv = call_factory(self.create_app, self) else: @@ -330,10 +332,14 @@ class ScriptInfo(object): 'the FLASK_APP environment variable.\n\nFor more ' 'information see ' 'http://flask.pocoo.org/docs/latest/quickstart/') + rv = locate_app(self, self.app_import_path) + debug = get_debug_flag() + if debug is not None: - rv.debug = debug + rv._reconfigure_for_run_debug(debug) + self._loaded_app = rv return rv From d75d83defdbcc2e498f816b5cd20dcc24a1b7138 Mon Sep 17 00:00:00 2001 From: "Eugene M. Kim" Date: Wed, 14 Jun 2017 11:30:42 -0700 Subject: [PATCH 290/399] Add UTs for #2372 test_encode_aware_datetime() fails for non-UTC timezones due to the bug. --- tests/test_helpers.py | 10 ++++++++++ tox.ini | 1 + 2 files changed, 11 insertions(+) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index c66e650b..9e679746 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -23,6 +23,7 @@ from werkzeug.http import parse_cache_control_header, parse_options_header from werkzeug.http import http_date from flask._compat import StringIO, text_type from flask.helpers import get_debug_flag, make_response +from pytz import timezone def has_encoding(name): @@ -177,6 +178,15 @@ class TestJSON(object): assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple()) + @pytest.mark.parametrize('tzname', ('UTC', 'PST8PDT', 'Asia/Seoul')) + def test_jsonify_aware_datetimes(self, tzname): + """Test if aware datetime.datetime objects are converted into GMT.""" + dt_naive = datetime.datetime(2017, 1, 1, 12, 34, 56) + dt_aware = timezone(tzname).localize(dt_naive) + dt_as_gmt = dt_aware.astimezone(timezone('GMT')) + expected = dt_as_gmt.strftime('"%a, %d %b %Y %H:%M:%S %Z"') + assert flask.json.JSONEncoder().encode(dt_aware) == expected + def test_jsonify_uuid_types(self, app, client): """Test jsonify with uuid.UUID types""" diff --git a/tox.ini b/tox.ini index cb6dd342..a0b4ad66 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,7 @@ deps = coverage greenlet blinker + pytz lowest: Werkzeug==0.9 lowest: Jinja2==2.4 From 4d2a3ab2e07e36b56d09e16f0836dbd0bdd7b34c Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 14 Jun 2017 12:31:56 -0700 Subject: [PATCH 291/399] test no debug flag doesn't reconfigure test templates_auto_reload property instead of config use app fixture in test --- tests/test_templating.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/test_templating.py b/tests/test_templating.py index 18143ba3..aaec54d7 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -386,19 +386,19 @@ def test_templates_auto_reload(app): app.config['TEMPLATES_AUTO_RELOAD'] = True assert app.jinja_env.auto_reload is True -def test_templates_auto_reload_debug_run(monkeypatch): - # debug is None in config, config option is None, app.run(debug=True) - # Mocks werkzeug.serving.run_simple method +def test_templates_auto_reload_debug_run(app, monkeypatch): def run_simple_mock(*args, **kwargs): pass - app = flask.Flask(__name__) monkeypatch.setattr(werkzeug.serving, 'run_simple', run_simple_mock) - assert app.config['TEMPLATES_AUTO_RELOAD'] is None - assert app.jinja_env.auto_reload is False + app.run() + assert app.templates_auto_reload == False + assert app.jinja_env.auto_reload == False + app.run(debug=True) - assert app.jinja_env.auto_reload is True + assert app.templates_auto_reload == True + assert app.jinja_env.auto_reload == True def test_template_loader_debugging(test_apps): From d41e2e6a5db3211ecd3c3f3f55934474406ffac2 Mon Sep 17 00:00:00 2001 From: "Eugene M. Kim" Date: Wed, 14 Jun 2017 11:12:11 -0700 Subject: [PATCH 292/399] Correctly encode aware, non-UTC datetime objects http_date() requires timetuple in UTC, but JSONEncoder.default() was passing a local timetuple instead. --- CHANGES | 2 ++ flask/json/__init__.py | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 59e8c4c1..f37dfc54 100644 --- a/CHANGES +++ b/CHANGES @@ -79,6 +79,7 @@ Major release, unreleased - Removed error handler caching because it caused unexpected results for some exception inheritance hierarchies. Register handlers explicitly for each exception if you don't want to traverse the MRO. (`#2362`_) +- Fix incorrect JSON encoding of aware, non-UTC datetimes. (`#2374`_) .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -102,6 +103,7 @@ Major release, unreleased .. _#2354: https://github.com/pallets/flask/pull/2354 .. _#2358: https://github.com/pallets/flask/pull/2358 .. _#2362: https://github.com/pallets/flask/pull/2362 +.. _#2374: https://github.com/pallets/flask/pull/2374 Version 0.12.2 -------------- diff --git a/flask/json/__init__.py b/flask/json/__init__.py index 93e6fdc4..6559c1aa 100644 --- a/flask/json/__init__.py +++ b/flask/json/__init__.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- import io import uuid -from datetime import date +from datetime import date, datetime from flask.globals import current_app, request from flask._compat import text_type, PY2 @@ -62,6 +62,8 @@ class JSONEncoder(_json.JSONEncoder): return list(iterable) return JSONEncoder.default(self, o) """ + if isinstance(o, datetime): + return http_date(o.utctimetuple()) if isinstance(o, date): return http_date(o.timetuple()) if isinstance(o, uuid.UUID): From 0e6cab357690614791ab4ca0da0ac65dbb803041 Mon Sep 17 00:00:00 2001 From: "Eugene M. Kim" Date: Wed, 14 Jun 2017 13:06:26 -0700 Subject: [PATCH 293/399] Rewrite test_jsonify_aware_datetimes without pytz --- tests/test_helpers.py | 15 +++++++-------- tox.ini | 1 - 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 9e679746..b1418b81 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -23,7 +23,6 @@ from werkzeug.http import parse_cache_control_header, parse_options_header from werkzeug.http import http_date from flask._compat import StringIO, text_type from flask.helpers import get_debug_flag, make_response -from pytz import timezone def has_encoding(name): @@ -178,14 +177,14 @@ class TestJSON(object): assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple()) - @pytest.mark.parametrize('tzname', ('UTC', 'PST8PDT', 'Asia/Seoul')) - def test_jsonify_aware_datetimes(self, tzname): + @pytest.mark.parametrize('tz', (('UTC', 0), ('PST', -8), ('KST', 9))) + def test_jsonify_aware_datetimes(self, tz): """Test if aware datetime.datetime objects are converted into GMT.""" - dt_naive = datetime.datetime(2017, 1, 1, 12, 34, 56) - dt_aware = timezone(tzname).localize(dt_naive) - dt_as_gmt = dt_aware.astimezone(timezone('GMT')) - expected = dt_as_gmt.strftime('"%a, %d %b %Y %H:%M:%S %Z"') - assert flask.json.JSONEncoder().encode(dt_aware) == expected + tzinfo = datetime.timezone(datetime.timedelta(hours=tz[1]), name=tz[0]) + dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo) + gmt = datetime.timezone(datetime.timedelta(), name='GMT') + expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"') + assert flask.json.JSONEncoder().encode(dt) == expected def test_jsonify_uuid_types(self, app, client): """Test jsonify with uuid.UUID types""" diff --git a/tox.ini b/tox.ini index a0b4ad66..cb6dd342 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,6 @@ deps = coverage greenlet blinker - pytz lowest: Werkzeug==0.9 lowest: Jinja2==2.4 From eb9618347c680a038e2e6310228d85a53b080f93 Mon Sep 17 00:00:00 2001 From: "Eugene M. Kim" Date: Wed, 14 Jun 2017 13:57:40 -0700 Subject: [PATCH 294/399] Use pytz again for tests This is because datetime.timezone is Python 3 only. The only alternative would be to hand-spin a datetime.tzinfo subclass, an overkill. This reverts commit 0e6cab357690614791ab4ca0da0ac65dbb803041. --- tests/test_helpers.py | 15 ++++++++------- tox.ini | 1 + 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index b1418b81..9e679746 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -23,6 +23,7 @@ from werkzeug.http import parse_cache_control_header, parse_options_header from werkzeug.http import http_date from flask._compat import StringIO, text_type from flask.helpers import get_debug_flag, make_response +from pytz import timezone def has_encoding(name): @@ -177,14 +178,14 @@ class TestJSON(object): assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple()) - @pytest.mark.parametrize('tz', (('UTC', 0), ('PST', -8), ('KST', 9))) - def test_jsonify_aware_datetimes(self, tz): + @pytest.mark.parametrize('tzname', ('UTC', 'PST8PDT', 'Asia/Seoul')) + def test_jsonify_aware_datetimes(self, tzname): """Test if aware datetime.datetime objects are converted into GMT.""" - tzinfo = datetime.timezone(datetime.timedelta(hours=tz[1]), name=tz[0]) - dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo) - gmt = datetime.timezone(datetime.timedelta(), name='GMT') - expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"') - assert flask.json.JSONEncoder().encode(dt) == expected + dt_naive = datetime.datetime(2017, 1, 1, 12, 34, 56) + dt_aware = timezone(tzname).localize(dt_naive) + dt_as_gmt = dt_aware.astimezone(timezone('GMT')) + expected = dt_as_gmt.strftime('"%a, %d %b %Y %H:%M:%S %Z"') + assert flask.json.JSONEncoder().encode(dt_aware) == expected def test_jsonify_uuid_types(self, app, client): """Test jsonify with uuid.UUID types""" diff --git a/tox.ini b/tox.ini index cb6dd342..a0b4ad66 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,7 @@ deps = coverage greenlet blinker + pytz lowest: Werkzeug==0.9 lowest: Jinja2==2.4 From 34050630d67918dc8614b8ead8c683e66e4ececc Mon Sep 17 00:00:00 2001 From: "Eugene M. Kim" Date: Wed, 14 Jun 2017 14:08:42 -0700 Subject: [PATCH 295/399] Skip aware datetime tests if pytz is unavailable --- tests/test_helpers.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 9e679746..313427d6 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -23,7 +23,12 @@ from werkzeug.http import parse_cache_control_header, parse_options_header from werkzeug.http import http_date from flask._compat import StringIO, text_type from flask.helpers import get_debug_flag, make_response -from pytz import timezone +try: + from pytz import timezone +except ImportError: + has_pytz = False +else: + has_pytz = True def has_encoding(name): @@ -178,6 +183,7 @@ class TestJSON(object): assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple()) + @pytest.mark.skipif('not has_pytz') @pytest.mark.parametrize('tzname', ('UTC', 'PST8PDT', 'Asia/Seoul')) def test_jsonify_aware_datetimes(self, tzname): """Test if aware datetime.datetime objects are converted into GMT.""" From f80376027571ba5190ab6cf1411dd1dd9bb9c65e Mon Sep 17 00:00:00 2001 From: "Eugene M. Kim" Date: Wed, 14 Jun 2017 14:14:18 -0700 Subject: [PATCH 296/399] Re-revert to not using pytz Will spin a tzinfo subclass. --- tests/test_helpers.py | 21 +++++++-------------- tox.ini | 1 - 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 313427d6..b1418b81 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -23,12 +23,6 @@ from werkzeug.http import parse_cache_control_header, parse_options_header from werkzeug.http import http_date from flask._compat import StringIO, text_type from flask.helpers import get_debug_flag, make_response -try: - from pytz import timezone -except ImportError: - has_pytz = False -else: - has_pytz = True def has_encoding(name): @@ -183,15 +177,14 @@ class TestJSON(object): assert rv.mimetype == 'application/json' assert flask.json.loads(rv.data)['x'] == http_date(d.timetuple()) - @pytest.mark.skipif('not has_pytz') - @pytest.mark.parametrize('tzname', ('UTC', 'PST8PDT', 'Asia/Seoul')) - def test_jsonify_aware_datetimes(self, tzname): + @pytest.mark.parametrize('tz', (('UTC', 0), ('PST', -8), ('KST', 9))) + def test_jsonify_aware_datetimes(self, tz): """Test if aware datetime.datetime objects are converted into GMT.""" - dt_naive = datetime.datetime(2017, 1, 1, 12, 34, 56) - dt_aware = timezone(tzname).localize(dt_naive) - dt_as_gmt = dt_aware.astimezone(timezone('GMT')) - expected = dt_as_gmt.strftime('"%a, %d %b %Y %H:%M:%S %Z"') - assert flask.json.JSONEncoder().encode(dt_aware) == expected + tzinfo = datetime.timezone(datetime.timedelta(hours=tz[1]), name=tz[0]) + dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo) + gmt = datetime.timezone(datetime.timedelta(), name='GMT') + expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"') + assert flask.json.JSONEncoder().encode(dt) == expected def test_jsonify_uuid_types(self, app, client): """Test jsonify with uuid.UUID types""" diff --git a/tox.ini b/tox.ini index a0b4ad66..cb6dd342 100644 --- a/tox.ini +++ b/tox.ini @@ -15,7 +15,6 @@ deps = coverage greenlet blinker - pytz lowest: Werkzeug==0.9 lowest: Jinja2==2.4 From 63ccdada1b9ddc69f97b00de1ac343894c1d8420 Mon Sep 17 00:00:00 2001 From: "Eugene M. Kim" Date: Wed, 14 Jun 2017 14:23:13 -0700 Subject: [PATCH 297/399] Actually hand-spin and use a tzinfo subclass This is for Python 2.x compatibility. Suggested-by: David Lord --- tests/test_helpers.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index b1418b81..73c6bee7 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -34,6 +34,27 @@ def has_encoding(name): return False +class FixedOffset(datetime.tzinfo): + """Fixed offset in hours east from UTC. + + This is a slight adaptation of the ``FixedOffset`` example found in + https://docs.python.org/2.7/library/datetime.html. + """ + + def __init__(self, hours, name): + self.__offset = datetime.timedelta(hours=hours) + self.__name = name + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return datetime.timedelta() + + class TestJSON(object): def test_ignore_cached_json(self, app): with app.test_request_context('/', method='POST', data='malformed', @@ -180,9 +201,9 @@ class TestJSON(object): @pytest.mark.parametrize('tz', (('UTC', 0), ('PST', -8), ('KST', 9))) def test_jsonify_aware_datetimes(self, tz): """Test if aware datetime.datetime objects are converted into GMT.""" - tzinfo = datetime.timezone(datetime.timedelta(hours=tz[1]), name=tz[0]) + tzinfo = FixedOffset(hours=tz[1], name=tz[0]) dt = datetime.datetime(2017, 1, 1, 12, 34, 56, tzinfo=tzinfo) - gmt = datetime.timezone(datetime.timedelta(), name='GMT') + gmt = FixedOffset(hours=0, name='GMT') expected = dt.astimezone(gmt).strftime('"%a, %d %b %Y %H:%M:%S %Z"') assert flask.json.JSONEncoder().encode(dt) == expected From 7c40aa9e50d97b7c412745a53c8cdc2597c46680 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Thu, 15 Jun 2017 11:27:50 -0700 Subject: [PATCH 298/399] Import app from wsgi.py or app.py if FLASK_APP is not defined Fixes #2376 --- flask/cli.py | 29 ++++++++++++++++++++--------- tests/test_apps/helloworld/hello.py | 6 ++++++ tests/test_apps/helloworld/wsgi.py | 1 + tests/test_cli.py | 22 +++++++++++++++++++++- 4 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 tests/test_apps/helloworld/hello.py create mode 100644 tests/test_apps/helloworld/wsgi.py diff --git a/flask/cli.py b/flask/cli.py index 4b5323c7..dbee18f0 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -172,11 +172,12 @@ def prepare_exec_for_file(filename): if not os.path.isfile(os.path.join(dirpath, '__init__.py')): break - sys.path.insert(0, dirpath) + if sys.path[0] != dirpath: + sys.path.insert(0, dirpath) return '.'.join(module[::-1]) -def locate_app(script_info, app_id): +def locate_app(script_info, app_id, raise_if_not_found=True): """Attempts to locate the application.""" __traceback_hide__ = True if ':' in app_id: @@ -193,12 +194,14 @@ def locate_app(script_info, app_id): if sys.exc_info()[-1].tb_next: stack_trace = traceback.format_exc() raise NoAppException('There was an error trying to import' - ' the app (%s):\n%s' % (module, stack_trace)) - else: + ' the app (%s):\n%s' % (module, stack_trace)) + elif raise_if_not_found: raise NoAppException('The file/path provided (%s) does not appear' ' to exist. Please verify the path is ' 'correct. If app is not on PYTHONPATH, ' 'ensure the extension is .py' % module) + else: + return mod = sys.modules[module] if app_obj is None: @@ -326,15 +329,23 @@ class ScriptInfo(object): if self.create_app is not None: rv = call_factory(self.create_app, self) else: - if not self.app_import_path: + if self.app_import_path: + rv = locate_app(self, self.app_import_path) + else: + for module in ['wsgi.py', 'app.py']: + import_path = prepare_exec_for_file(module) + rv = locate_app(self, import_path, + raise_if_not_found=False) + if rv: + break + if not rv: raise NoAppException( 'Could not locate Flask application. You did not provide ' - 'the FLASK_APP environment variable.\n\nFor more ' - 'information see ' + 'the FLASK_APP environment variable, and a wsgi.py or ' + 'app.py module was not found in the current directory.\n\n' + 'For more information see ' 'http://flask.pocoo.org/docs/latest/quickstart/') - rv = locate_app(self, self.app_import_path) - debug = get_debug_flag() if debug is not None: diff --git a/tests/test_apps/helloworld/hello.py b/tests/test_apps/helloworld/hello.py new file mode 100644 index 00000000..bbf7e467 --- /dev/null +++ b/tests/test_apps/helloworld/hello.py @@ -0,0 +1,6 @@ +from flask import Flask +app = Flask(__name__) + +@app.route("/") +def hello(): + return "Hello World!" diff --git a/tests/test_apps/helloworld/wsgi.py b/tests/test_apps/helloworld/wsgi.py new file mode 100644 index 00000000..fab4048b --- /dev/null +++ b/tests/test_apps/helloworld/wsgi.py @@ -0,0 +1 @@ +from hello import app diff --git a/tests/test_cli.py b/tests/test_cli.py index 899fb1f0..65993920 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -181,6 +181,8 @@ def test_locate_app(test_apps): script_info, "cliapp.factory:create_app ()") pytest.raises( NoAppException, locate_app, script_info, "cliapp.importerrorapp") + assert locate_app(script_info, "notanpp.py", + raise_if_not_found=False) is None def test_find_default_import_path(test_apps, monkeypatch, tmpdir): @@ -214,7 +216,7 @@ def test_get_version(test_apps, capsys): assert py_ver in out -def test_scriptinfo(test_apps): +def test_scriptinfo(test_apps, monkeypatch): """Test of ScriptInfo.""" obj = ScriptInfo(app_import_path="cliapp.app:testapp") assert obj.load_app().name == "testapp" @@ -228,6 +230,24 @@ def test_scriptinfo(test_apps): assert app.name == "createapp" assert obj.load_app() == app + obj = ScriptInfo() + pytest.raises( + NoAppException, obj.load_app) + + # import app from wsgi.py in current directory + monkeypatch.chdir(os.path.abspath( + os.path.join(os.path.dirname(__file__), 'test_apps', 'helloworld'))) + obj = ScriptInfo() + app = obj.load_app() + assert app.name == 'hello' + + # import app from app.py in current directory + monkeypatch.chdir(os.path.abspath( + os.path.join(os.path.dirname(__file__), 'test_apps', 'cliapp'))) + obj = ScriptInfo() + app = obj.load_app() + assert app.name == 'testapp' + def test_with_appcontext(runner): """Test of with_appcontext.""" From 0b80acb25cb4ee3f2acad6ae8064d834e0583408 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Thu, 15 Jun 2017 12:15:38 -0700 Subject: [PATCH 299/399] document wsgi.py and app.py default modules --- docs/cli.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 1c06f343..8fbfe962 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -26,9 +26,13 @@ built-in commands that are always there. Flask extensions can also register more commands there if they desire so. For the :command:`flask` script to work, an application needs to be -discovered. This is achieved by exporting the ``FLASK_APP`` environment -variable. It can be either set to an import path or to a filename of a -Python module that contains a Flask application. +discovered. Flask looks for a module named wsgi.py or app.py by default, and +if it finds one it assumes the application is defined in it. + +You can instruct Flask to look for the application in a different module by +exporting the ``FLASK_APP`` environment variable. It can be either set to an +import path or to a filename of a Python module that contains a Flask +application. In that imported file the name of the app needs to be called ``app`` or optionally be specified after a colon. For instance From 448368e226c8edd1077cf8e5a12ddaa9222b8ec9 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 16 Jun 2017 06:59:37 -0700 Subject: [PATCH 300/399] style cleanup [ci skip] --- docs/cli.rst | 8 ++++---- flask/cli.py | 30 +++++++++++++++++++++--------- tests/test_cli.py | 18 ++++++++++-------- 3 files changed, 35 insertions(+), 21 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 8fbfe962..52885f43 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -25,12 +25,12 @@ 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 desire so. -For the :command:`flask` script to work, an application needs to be -discovered. Flask looks for a module named wsgi.py or app.py by default, and -if it finds one it assumes the application is defined in it. +For the :command:`flask` script to work, an application needs to be discovered. +Flask looks for a module named :file:`wsgi.py` or :file:`app.py` by default, +and if it finds one it assumes the application is defined in it. You can instruct Flask to look for the application in a different module by -exporting the ``FLASK_APP`` environment variable. It can be either set to an +exporting the ``FLASK_APP`` environment variable. It can be either set to an import path or to a filename of a Python module that contains a Flask application. diff --git a/flask/cli.py b/flask/cli.py index dbee18f0..536f53ee 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -174,12 +174,14 @@ def prepare_exec_for_file(filename): if sys.path[0] != dirpath: sys.path.insert(0, dirpath) + return '.'.join(module[::-1]) def locate_app(script_info, app_id, raise_if_not_found=True): """Attempts to locate the application.""" __traceback_hide__ = True + if ':' in app_id: module, app_obj = app_id.split(':', 1) else: @@ -193,17 +195,23 @@ def locate_app(script_info, app_id, raise_if_not_found=True): # Determine this by checking whether the trace has a depth > 1. if sys.exc_info()[-1].tb_next: stack_trace = traceback.format_exc() - raise NoAppException('There was an error trying to import' - ' the app (%s):\n%s' % (module, stack_trace)) + raise NoAppException( + 'There was an error trying to import the app ({module}):\n' + '{stack_trace}'.format( + module=module, stack_trace=stack_trace + ) + ) elif raise_if_not_found: - raise NoAppException('The file/path provided (%s) does not appear' - ' to exist. Please verify the path is ' - 'correct. If app is not on PYTHONPATH, ' - 'ensure the extension is .py' % module) + raise NoAppException( + 'The file/path provided (%s) does not appear to exist. Please' + ' verify the path is correct. If app is not on PYTHONPATH,' + ' ensure the extension is .py.'.format(module=module) + ) else: return mod = sys.modules[module] + if app_obj is None: return find_best_app(script_info, mod) else: @@ -334,17 +342,21 @@ class ScriptInfo(object): else: for module in ['wsgi.py', 'app.py']: import_path = prepare_exec_for_file(module) - rv = locate_app(self, import_path, - raise_if_not_found=False) + rv = locate_app( + self, import_path, raise_if_not_found=False + ) + if rv: break + if not rv: raise NoAppException( 'Could not locate Flask application. You did not provide ' 'the FLASK_APP environment variable, and a wsgi.py or ' 'app.py module was not found in the current directory.\n\n' 'For more information see ' - 'http://flask.pocoo.org/docs/latest/quickstart/') + 'http://flask.pocoo.org/docs/latest/quickstart/' + ) debug = get_debug_flag() diff --git a/tests/test_cli.py b/tests/test_cli.py index 65993920..0e0a56ad 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -181,8 +181,9 @@ def test_locate_app(test_apps): script_info, "cliapp.factory:create_app ()") pytest.raises( NoAppException, locate_app, script_info, "cliapp.importerrorapp") - assert locate_app(script_info, "notanpp.py", - raise_if_not_found=False) is None + assert locate_app( + script_info, "notanpp.py", raise_if_not_found=False + ) is None def test_find_default_import_path(test_apps, monkeypatch, tmpdir): @@ -231,19 +232,20 @@ def test_scriptinfo(test_apps, monkeypatch): assert obj.load_app() == app obj = ScriptInfo() - pytest.raises( - NoAppException, obj.load_app) + pytest.raises(NoAppException, obj.load_app) # import app from wsgi.py in current directory - monkeypatch.chdir(os.path.abspath( - os.path.join(os.path.dirname(__file__), 'test_apps', 'helloworld'))) + monkeypatch.chdir(os.path.abspath(os.path.join( + os.path.dirname(__file__), 'test_apps', 'helloworld' + ))) obj = ScriptInfo() app = obj.load_app() assert app.name == 'hello' # import app from app.py in current directory - monkeypatch.chdir(os.path.abspath( - os.path.join(os.path.dirname(__file__), 'test_apps', 'cliapp'))) + monkeypatch.chdir(os.path.abspath(os.path.join( + os.path.dirname(__file__), 'test_apps', 'cliapp' + ))) obj = ScriptInfo() app = obj.load_app() assert app.name == 'testapp' From bfc2ee7dfdfdb5fd68be208babd896f93194b7b4 Mon Sep 17 00:00:00 2001 From: trirpi Date: Fri, 16 Jun 2017 21:15:33 +0200 Subject: [PATCH 301/399] removed bug in memory sqlite uri --- docs/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 186a3695..7280fae2 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -475,7 +475,7 @@ configuration:: class Config(object): DEBUG = False TESTING = False - DATABASE_URI = 'sqlite://:memory:' + DATABASE_URI = 'sqlite:///:memory:' class ProductionConfig(Config): DATABASE_URI = 'mysql://user@localhost/foo' From 75327c0a8500ca2e90e974dfda2ab8e170de65ff Mon Sep 17 00:00:00 2001 From: John Moutafis Date: Fri, 23 Jun 2017 17:53:38 +0300 Subject: [PATCH 302/399] Remove deprecated init_jinja_globals. --- flask/app.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/flask/app.py b/flask/app.py index 04c39547..e2f8604a 100644 --- a/flask/app.py +++ b/flask/app.py @@ -772,15 +772,6 @@ class Flask(_PackageBoundObject): """ return DispatchingJinjaLoader(self) - def init_jinja_globals(self): - """Deprecated. Used to initialize the Jinja2 globals. - - .. versionadded:: 0.5 - .. versionchanged:: 0.7 - This method is deprecated with 0.7. Override - :meth:`create_jinja_environment` instead. - """ - def select_jinja_autoescape(self, filename): """Returns ``True`` if autoescaping should be active for the given template name. If no template name is given, returns `True`. From e9386a72738bfc77cbfbce6c648f8b638dd7b836 Mon Sep 17 00:00:00 2001 From: John Moutafis Date: Mon, 26 Jun 2017 11:48:29 +0300 Subject: [PATCH 303/399] Remove deprecated request_globals_class _get and _set methods. --- flask/app.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/flask/app.py b/flask/app.py index e2f8604a..a5d7cfaf 100644 --- a/flask/app.py +++ b/flask/app.py @@ -188,18 +188,6 @@ class Flask(_PackageBoundObject): #: .. versionadded:: 0.10 app_ctx_globals_class = _AppCtxGlobals - # Backwards compatibility support - def _get_request_globals_class(self): - return self.app_ctx_globals_class - def _set_request_globals_class(self, value): - from warnings import warn - warn(DeprecationWarning('request_globals_class attribute is now ' - 'called app_ctx_globals_class')) - self.app_ctx_globals_class = value - request_globals_class = property(_get_request_globals_class, - _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`. #: From 9491bf8695be9b24036b3d966d9c43b46f0ddc23 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 26 Jun 2017 07:39:35 -0700 Subject: [PATCH 304/399] remove deprecated Flask.error_handlers --- flask/app.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/flask/app.py b/flask/app.py index a5d7cfaf..b8e5d0b2 100644 --- a/flask/app.py +++ b/flask/app.py @@ -393,10 +393,6 @@ class Flask(_PackageBoundObject): #: To register a view function, use the :meth:`route` decorator. self.view_functions = {} - # support for the now deprecated `error_handlers` attribute. The - # :attr:`error_handler_spec` shall be used now. - self._error_handlers = {} - #: 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 @@ -407,7 +403,7 @@ class Flask(_PackageBoundObject): #: #: To register an error handler, use the :meth:`errorhandler` #: decorator. - self.error_handler_spec = {None: self._error_handlers} + self.error_handler_spec = {} #: A list of functions that are called when :meth:`url_for` raises a #: :exc:`~werkzeug.routing.BuildError`. Each function registered here @@ -563,17 +559,6 @@ class Flask(_PackageBoundObject): #: This is an instance of a :class:`click.Group` object. self.cli = cli.AppGroup(self.name) - def _get_error_handlers(self): - from warnings import warn - warn(DeprecationWarning('error_handlers is deprecated, use the ' - 'new error_handler_spec attribute instead.'), stacklevel=1) - return self._error_handlers - def _set_error_handlers(self, value): - self._error_handlers = value - self.error_handler_spec[None] = value - error_handlers = property(_get_error_handlers, _set_error_handlers) - del _get_error_handlers, _set_error_handlers - @locked_cached_property def name(self): """The name of the application. This is usually the import name From d63c2bc4178b0a371ee53c9d94b73c7be3174c9d Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 26 Jun 2017 07:42:17 -0700 Subject: [PATCH 305/399] remove deprecated Flask.static_path --- flask/app.py | 48 ++++++++++++++++++++++++++++----------------- tests/test_basic.py | 13 ------------ 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/flask/app.py b/flask/app.py index b8e5d0b2..19d23f0d 100644 --- a/flask/app.py +++ b/flask/app.py @@ -349,29 +349,38 @@ class Flask(_PackageBoundObject): #: resources contained in the package. root_path = None - def __init__(self, import_name, static_path=None, static_url_path=None, - static_folder='static', static_host=None, - host_matching=False, template_folder='templates', - instance_path=None, instance_relative_config=False, - root_path=None): - _PackageBoundObject.__init__(self, import_name, - 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 ' - 'static_url_path'), stacklevel=2) - static_url_path = static_path + def __init__( + self, + import_name, + static_url_path=None, + static_folder='static', + static_host=None, + host_matching=False, + template_folder='templates', + instance_path=None, + instance_relative_config=False, + root_path=None + ): + _PackageBoundObject.__init__( + self, + import_name, + template_folder=template_folder, + root_path=root_path + ) if static_url_path is not None: self.static_url_path = static_url_path + if static_folder is not None: self.static_folder = static_folder + if instance_path is None: instance_path = self.auto_find_instance_path() elif not os.path.isabs(instance_path): - raise ValueError('If an instance path is provided it must be ' - 'absolute. A relative path was given instead.') + raise ValueError( + 'If an instance path is provided it must be absolute.' + ' A relative path was given instead.' + ) #: Holds the path to the instance folder. #: @@ -547,9 +556,12 @@ class Flask(_PackageBoundObject): # development). Also, Google App Engine stores static files somewhere if self.has_static_folder: assert bool(static_host) == host_matching, 'Invalid static_host/host_matching combination' - self.add_url_rule(self.static_url_path + '/', - endpoint='static', host=static_host, - view_func=self.send_static_file) + self.add_url_rule( + self.static_url_path + '/', + endpoint='static', + host=static_host, + view_func=self.send_static_file + ) #: The click command line context for this application. Commands #: registered here show up in the :command:`flask` command once the diff --git a/tests/test_basic.py b/tests/test_basic.py index d24678c2..be097eb9 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -1353,19 +1353,6 @@ def test_static_files(app, client): rv.close() -def test_static_path_deprecated(recwarn): - app = flask.Flask(__name__, static_path='/foo') - recwarn.pop(DeprecationWarning) - - app.testing = True - rv = app.test_client().get('/foo/index.html') - assert rv.status_code == 200 - rv.close() - - with app.test_request_context(): - assert flask.url_for('static', filename='index.html') == '/foo/index.html' - - def test_static_url_path(): app = flask.Flask(__name__, static_url_path='/foo') app.testing = True From 723e6650048bc8097da20b23ebddcd4c0ad6bc61 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 26 Jun 2017 07:46:33 -0700 Subject: [PATCH 306/399] remove deprecated Request.module --- flask/helpers.py | 38 +++++++++++++++++++------------------- flask/wrappers.py | 17 ----------------- tests/test_deprecations.py | 13 ------------- 3 files changed, 19 insertions(+), 49 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index 94c5ce77..5adb0e2a 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -268,40 +268,40 @@ def url_for(endpoint, **values): """ appctx = _app_ctx_stack.top reqctx = _request_ctx_stack.top + if appctx is None: - raise RuntimeError('Attempted to generate a URL without the ' - 'application context being pushed. This has to be ' - 'executed when application context is available.') + raise RuntimeError( + 'Attempted to generate a URL without the application context being' + ' pushed. This has to be executed when application context is' + ' available.' + ) # If request specific information is available we have some extra # features that support "relative" URLs. if reqctx is not None: url_adapter = reqctx.url_adapter blueprint_name = request.blueprint - if not reqctx.request._is_old_module: - if endpoint[:1] == '.': - if blueprint_name is not None: - endpoint = blueprint_name + endpoint - else: - endpoint = endpoint[1:] - else: - # TODO: get rid of this deprecated functionality in 1.0 - if '.' not in endpoint: - if blueprint_name is not None: - endpoint = blueprint_name + '.' + endpoint - elif endpoint.startswith('.'): + + if endpoint[:1] == '.': + if blueprint_name is not None: + endpoint = blueprint_name + endpoint + else: endpoint = endpoint[1:] + external = values.pop('_external', False) # Otherwise go with the url adapter from the appctx and make # the URLs external by default. else: url_adapter = appctx.url_adapter + if url_adapter is None: - raise RuntimeError('Application was not able to create a URL ' - 'adapter for request independent URL generation. ' - 'You might be able to fix this by setting ' - 'the SERVER_NAME config variable.') + raise RuntimeError( + 'Application was not able to create a URL adapter for request' + ' independent URL generation. You might be able to fix this by' + ' setting the SERVER_NAME config variable.' + ) + external = values.pop('_external', True) anchor = values.pop('_anchor', None) diff --git a/flask/wrappers.py b/flask/wrappers.py index 918b0a93..4dde5f3c 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -141,10 +141,6 @@ class Request(RequestBase, JSONMixin): #: something similar. routing_exception = None - # Switched by the request context until 1.0 to opt in deprecated - # module functionality. - _is_old_module = False - @property def max_content_length(self): """Read-only view of the ``MAX_CONTENT_LENGTH`` config key.""" @@ -161,19 +157,6 @@ class Request(RequestBase, JSONMixin): if self.url_rule is not None: return self.url_rule.endpoint - @property - def module(self): - """The name of the current module if the request was dispatched - to an actual module. This is deprecated functionality, use blueprints - instead. - """ - from warnings import warn - warn(DeprecationWarning('modules were deprecated in favor of ' - 'blueprints. Use request.blueprint ' - 'instead.'), stacklevel=2) - if self._is_old_module: - return self.blueprint - @property def blueprint(self): """The name of the current blueprint""" diff --git a/tests/test_deprecations.py b/tests/test_deprecations.py index 6ab63dd4..5dfff127 100644 --- a/tests/test_deprecations.py +++ b/tests/test_deprecations.py @@ -9,8 +9,6 @@ :license: BSD, see LICENSE for more details. """ -import pytest - import flask @@ -26,14 +24,3 @@ class TestRequestDeprecation(object): client.post('/', data='{"spam": 42}', content_type='application/json') recwarn.pop(DeprecationWarning) - - def test_request_module(self, recwarn, app, client): - """Request.module is deprecated""" - - @app.route('/') - def index(): - assert flask.request.module is None - return 'OK' - - client.get('/') - recwarn.pop(DeprecationWarning) From 89c87240b6ef4f9663b4bb351d66cb8a50460795 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 26 Jun 2017 08:07:54 -0700 Subject: [PATCH 307/399] changelog for deprecations --- CHANGES | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGES b/CHANGES index f0cb9723..18ccf0b9 100644 --- a/CHANGES +++ b/CHANGES @@ -82,6 +82,16 @@ Major release, unreleased - Fix incorrect JSON encoding of aware, non-UTC datetimes. (`#2374`_) - Template auto reloading will honor the ``run`` command's ``debug`` flag even if ``app.jinja_env`` was already accessed. (`#2373`_) +- The following old deprecated code was removed. (`#2385`_) + + - ``Flask.init_jinja_globals`` - extend ``Flask.create_jinja_environment`` + instead. + - ``Flask.error_handlers`` - tracked by ``Flask.error_handler_spec``, + use ``@app.errorhandler`` to register handlers. + - ``Flask.request_globals_class`` - use ``Flask.app_ctx_globals_class`` + instead. + - ``Flask.static_path`` - use ``Flask.static_url_path`` instead. + - ``Request.module`` - use ``Request.blueprint`` instead. .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 @@ -107,6 +117,7 @@ Major release, unreleased .. _#2362: https://github.com/pallets/flask/pull/2362 .. _#2374: https://github.com/pallets/flask/pull/2374 .. _#2373: https://github.com/pallets/flask/pull/2373 +.. _#2385: https://github.com/pallets/flask/issues/2385 Version 0.12.2 -------------- From 715a9a3e7bd65421c36428ba9d4a1ee06b98bbc1 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 26 Jun 2017 08:47:28 -0700 Subject: [PATCH 308/399] remove deprecated flask.ext --- CHANGES | 3 + flask/ext/__init__.py | 29 ------ flask/exthook.py | 143 --------------------------- scripts/flaskext_compat.py | 125 ----------------------- setup.py | 2 +- tests/test_ext.py | 197 ------------------------------------- 6 files changed, 4 insertions(+), 495 deletions(-) delete mode 100644 flask/ext/__init__.py delete mode 100644 flask/exthook.py delete mode 100644 scripts/flaskext_compat.py delete mode 100644 tests/test_ext.py diff --git a/CHANGES b/CHANGES index 18ccf0b9..7280e469 100644 --- a/CHANGES +++ b/CHANGES @@ -84,6 +84,9 @@ Major release, unreleased if ``app.jinja_env`` was already accessed. (`#2373`_) - The following old deprecated code was removed. (`#2385`_) + - ``flask.ext`` - import extensions directly by their name instead of + through the ``flask.ext`` namespace. For example, + ``import flask.ext.sqlalchemy`` becomes ``import flask_sqlalchemy``. - ``Flask.init_jinja_globals`` - extend ``Flask.create_jinja_environment`` instead. - ``Flask.error_handlers`` - tracked by ``Flask.error_handler_spec``, diff --git a/flask/ext/__init__.py b/flask/ext/__init__.py deleted file mode 100644 index 051f44ac..00000000 --- a/flask/ext/__init__.py +++ /dev/null @@ -1,29 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.ext - ~~~~~~~~~ - - Redirect imports for extensions. This module basically makes it possible - for us to transition from flaskext.foo to flask_foo without having to - force all extensions to upgrade at the same time. - - When a user does ``from flask.ext.foo import bar`` it will attempt to - import ``from flask_foo import bar`` first and when that fails it will - try to import ``from flaskext.foo import bar``. - - We're switching from namespace packages because it was just too painful for - everybody involved. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - - -def setup(): - from ..exthook import ExtensionImporter - importer = ExtensionImporter(['flask_%s', 'flaskext.%s'], __name__) - importer.install() - - -setup() -del setup diff --git a/flask/exthook.py b/flask/exthook.py deleted file mode 100644 index d8842802..00000000 --- a/flask/exthook.py +++ /dev/null @@ -1,143 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.exthook - ~~~~~~~~~~~~~ - - Redirect imports for extensions. This module basically makes it possible - for us to transition from flaskext.foo to flask_foo without having to - force all extensions to upgrade at the same time. - - When a user does ``from flask.ext.foo import bar`` it will attempt to - import ``from flask_foo import bar`` first and when that fails it will - try to import ``from flaskext.foo import bar``. - - We're switching from namespace packages because it was just too painful for - everybody involved. - - This is used by `flask.ext`. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import sys -import os -import warnings -from ._compat import reraise - - -class ExtDeprecationWarning(DeprecationWarning): - pass - -warnings.simplefilter('always', ExtDeprecationWarning) - - -class ExtensionImporter(object): - """This importer redirects imports from this submodule to other locations. - This makes it possible to transition from the old flaskext.name to the - newer flask_name without people having a hard time. - """ - - def __init__(self, module_choices, wrapper_module): - self.module_choices = module_choices - self.wrapper_module = wrapper_module - self.prefix = wrapper_module + '.' - self.prefix_cutoff = wrapper_module.count('.') + 1 - - def __eq__(self, other): - return self.__class__.__module__ == other.__class__.__module__ and \ - self.__class__.__name__ == other.__class__.__name__ and \ - self.wrapper_module == other.wrapper_module and \ - self.module_choices == other.module_choices - - def __ne__(self, other): - return not self.__eq__(other) - - def install(self): - sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self] - - def find_module(self, fullname, path=None): - if fullname.startswith(self.prefix) and \ - fullname != 'flask.ext.ExtDeprecationWarning': - return self - - def load_module(self, fullname): - if fullname in sys.modules: - return sys.modules[fullname] - - modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff] - - warnings.warn( - "Importing flask.ext.{x} is deprecated, use flask_{x} instead." - .format(x=modname), ExtDeprecationWarning, stacklevel=2 - ) - - for path in self.module_choices: - realname = path % modname - try: - __import__(realname) - except ImportError: - exc_type, exc_value, tb = sys.exc_info() - # since we only establish the entry in sys.modules at the - # very this seems to be redundant, but if recursive imports - # happen we will call into the move import a second time. - # On the second invocation we still don't have an entry for - # fullname in sys.modules, but we will end up with the same - # fake module name and that import will succeed since this - # one already has a temporary entry in the modules dict. - # Since this one "succeeded" temporarily that second - # invocation now will have created a fullname entry in - # sys.modules which we have to kill. - sys.modules.pop(fullname, None) - - # If it's an important traceback we reraise it, otherwise - # we swallow it and try the next choice. The skipped frame - # is the one from __import__ above which we don't care about - if self.is_important_traceback(realname, tb): - reraise(exc_type, exc_value, tb.tb_next) - continue - module = sys.modules[fullname] = sys.modules[realname] - if '.' not in modname: - setattr(sys.modules[self.wrapper_module], modname, module) - - if realname.startswith('flaskext.'): - warnings.warn( - "Detected extension named flaskext.{x}, please rename it " - "to flask_{x}. The old form is deprecated." - .format(x=modname), ExtDeprecationWarning - ) - - return module - raise ImportError('No module named %s' % fullname) - - def is_important_traceback(self, important_module, tb): - """Walks a traceback's frames and checks if any of the frames - originated in the given important module. If that is the case then we - were able to import the module itself but apparently something went - wrong when the module was imported. (Eg: import of an import failed). - """ - while tb is not None: - if self.is_important_frame(important_module, tb): - return True - tb = tb.tb_next - return False - - def is_important_frame(self, important_module, tb): - """Checks a single frame if it's important.""" - g = tb.tb_frame.f_globals - if '__name__' not in g: - return False - - module_name = g['__name__'] - - # Python 2.7 Behavior. Modules are cleaned up late so the - # name shows up properly here. Success! - if module_name == important_module: - return True - - # Some python versions will clean up modules so early that the - # module name at that point is no longer set. Try guessing from - # the filename then. - filename = os.path.abspath(tb.tb_frame.f_code.co_filename) - test_string = os.path.sep + important_module.replace('.', os.path.sep) - return test_string + '.py' in filename or \ - test_string + os.path.sep + '__init__.py' in filename diff --git a/scripts/flaskext_compat.py b/scripts/flaskext_compat.py deleted file mode 100644 index 77d38c20..00000000 --- a/scripts/flaskext_compat.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flaskext_compat - ~~~~~~~~~~~~~~~ - - Implements the ``flask.ext`` virtual package for versions of Flask - older than 0.7. This module is a noop if Flask 0.8 was detected. - - Usage:: - - import flaskext_compat - flaskext_compat.activate() - from flask.ext import foo - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" -import types -import sys -import os - - -class ExtensionImporter(object): - """This importer redirects imports from this submodule to other locations. - This makes it possible to transition from the old flaskext.name to the - newer flask_name without people having a hard time. - """ - - def __init__(self, module_choices, wrapper_module): - self.module_choices = module_choices - self.wrapper_module = wrapper_module - self.prefix = wrapper_module + '.' - self.prefix_cutoff = wrapper_module.count('.') + 1 - - def __eq__(self, other): - return self.__class__.__module__ == other.__class__.__module__ and \ - self.__class__.__name__ == other.__class__.__name__ and \ - self.wrapper_module == other.wrapper_module and \ - self.module_choices == other.module_choices - - def __ne__(self, other): - return not self.__eq__(other) - - def install(self): - sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self] - - def find_module(self, fullname, path=None): - if fullname.startswith(self.prefix): - return self - - def load_module(self, fullname): - if fullname in sys.modules: - return sys.modules[fullname] - modname = fullname.split('.', self.prefix_cutoff)[self.prefix_cutoff] - for path in self.module_choices: - realname = path % modname - try: - __import__(realname) - except ImportError: - exc_type, exc_value, tb = sys.exc_info() - # since we only establish the entry in sys.modules at the - # end this seems to be redundant, but if recursive imports - # happen we will call into the move import a second time. - # On the second invocation we still don't have an entry for - # fullname in sys.modules, but we will end up with the same - # fake module name and that import will succeed since this - # one already has a temporary entry in the modules dict. - # Since this one "succeeded" temporarily that second - # invocation now will have created a fullname entry in - # sys.modules which we have to kill. - sys.modules.pop(fullname, None) - - # If it's an important traceback we reraise it, otherwise - # we swallow it and try the next choice. The skipped frame - # is the one from __import__ above which we don't care about. - if self.is_important_traceback(realname, tb): - raise exc_type, exc_value, tb.tb_next - continue - module = sys.modules[fullname] = sys.modules[realname] - if '.' not in modname: - setattr(sys.modules[self.wrapper_module], modname, module) - return module - raise ImportError('No module named %s' % fullname) - - def is_important_traceback(self, important_module, tb): - """Walks a traceback's frames and checks if any of the frames - originated in the given important module. If that is the case then we - were able to import the module itself but apparently something went - wrong when the module was imported. (Eg: import of an import failed). - """ - while tb is not None: - if self.is_important_frame(important_module, tb): - return True - tb = tb.tb_next - return False - - def is_important_frame(self, important_module, tb): - """Checks a single frame if it's important.""" - g = tb.tb_frame.f_globals - if '__name__' not in g: - return False - - module_name = g['__name__'] - - # Python 2.7 Behavior. Modules are cleaned up late so the - # name shows up properly here. Success! - if module_name == important_module: - return True - - # Some python versions will clean up modules so early that the - # module name at that point is no longer set. Try guessing from - # the filename then. - filename = os.path.abspath(tb.tb_frame.f_code.co_filename) - test_string = os.path.sep + important_module.replace('.', os.path.sep) - return test_string + '.py' in filename or \ - test_string + os.path.sep + '__init__.py' in filename - - -def activate(): - import flask - ext_module = types.ModuleType('flask.ext') - ext_module.__path__ = [] - flask.ext = sys.modules['flask.ext'] = ext_module - importer = ExtensionImporter(['flask_%s', 'flaskext.%s'], 'flask.ext') - importer.install() diff --git a/setup.py b/setup.py index a829dd8a..acade846 100644 --- a/setup.py +++ b/setup.py @@ -64,7 +64,7 @@ setup( description='A microframework based on Werkzeug, Jinja2 ' 'and good intentions', long_description=__doc__, - packages=['flask', 'flask.ext', 'flask.json'], + packages=['flask', 'flask.json'], include_package_data=True, zip_safe=False, platforms='any', diff --git a/tests/test_ext.py b/tests/test_ext.py deleted file mode 100644 index 48214905..00000000 --- a/tests/test_ext.py +++ /dev/null @@ -1,197 +0,0 @@ -# -*- coding: utf-8 -*- -""" - tests.ext - ~~~~~~~~~~~~~~~~~~~ - - Tests the extension import thing. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import sys -import pytest - -try: - from imp import reload as reload_module -except ImportError: - reload_module = reload - -from flask._compat import PY2 - - -@pytest.fixture(autouse=True) -def disable_extwarnings(recwarn): - from flask.exthook import ExtDeprecationWarning - - yield - - assert set(w.category for w in recwarn.list) \ - <= set([ExtDeprecationWarning]) - recwarn.clear() - - -@pytest.fixture(autouse=True) -def importhook_setup(monkeypatch): - # we clear this out for various reasons. The most important one is - # that a real flaskext could be in there which would disable our - # fake package. Secondly we want to make sure that the flaskext - # import hook does not break on reloading. - for entry, value in list(sys.modules.items()): - if ( - entry.startswith('flask.ext.') or - entry.startswith('flask_') or - entry.startswith('flaskext.') or - entry == 'flaskext' - ) and value is not None: - monkeypatch.delitem(sys.modules, entry) - from flask import ext - reload_module(ext) - - # reloading must not add more hooks - import_hooks = 0 - for item in sys.meta_path: - cls = type(item) - if cls.__module__ == 'flask.exthook' and \ - cls.__name__ == 'ExtensionImporter': - import_hooks += 1 - assert import_hooks == 1 - - yield - - from flask import ext - for key in ext.__dict__: - assert '.' not in key - - -@pytest.fixture -def newext_simple(modules_tmpdir): - x = modules_tmpdir.join('flask_newext_simple.py') - x.write('ext_id = "newext_simple"') - - -@pytest.fixture -def oldext_simple(modules_tmpdir): - flaskext = modules_tmpdir.mkdir('flaskext') - flaskext.join('__init__.py').write('\n') - flaskext.join('oldext_simple.py').write('ext_id = "oldext_simple"') - - -@pytest.fixture -def newext_package(modules_tmpdir): - pkg = modules_tmpdir.mkdir('flask_newext_package') - pkg.join('__init__.py').write('ext_id = "newext_package"') - pkg.join('submodule.py').write('def test_function():\n return 42\n') - - -@pytest.fixture -def oldext_package(modules_tmpdir): - flaskext = modules_tmpdir.mkdir('flaskext') - flaskext.join('__init__.py').write('\n') - oldext = flaskext.mkdir('oldext_package') - oldext.join('__init__.py').write('ext_id = "oldext_package"') - oldext.join('submodule.py').write('def test_function():\n' - ' return 42') - - -@pytest.fixture -def flaskext_broken(modules_tmpdir): - ext = modules_tmpdir.mkdir('flask_broken') - ext.join('b.py').write('\n') - ext.join('__init__.py').write('import flask.ext.broken.b\n' - 'import missing_module') - - -def test_flaskext_new_simple_import_normal(newext_simple): - from flask.ext.newext_simple import ext_id - assert ext_id == 'newext_simple' - - -def test_flaskext_new_simple_import_module(newext_simple): - from flask.ext import newext_simple - assert newext_simple.ext_id == 'newext_simple' - assert newext_simple.__name__ == 'flask_newext_simple' - - -def test_flaskext_new_package_import_normal(newext_package): - from flask.ext.newext_package import ext_id - assert ext_id == 'newext_package' - - -def test_flaskext_new_package_import_module(newext_package): - from flask.ext import newext_package - assert newext_package.ext_id == 'newext_package' - assert newext_package.__name__ == 'flask_newext_package' - - -def test_flaskext_new_package_import_submodule_function(newext_package): - from flask.ext.newext_package.submodule import test_function - assert test_function() == 42 - - -def test_flaskext_new_package_import_submodule(newext_package): - from flask.ext.newext_package import submodule - assert submodule.__name__ == 'flask_newext_package.submodule' - assert submodule.test_function() == 42 - - -def test_flaskext_old_simple_import_normal(oldext_simple): - from flask.ext.oldext_simple import ext_id - assert ext_id == 'oldext_simple' - - -def test_flaskext_old_simple_import_module(oldext_simple): - from flask.ext import oldext_simple - assert oldext_simple.ext_id == 'oldext_simple' - assert oldext_simple.__name__ == 'flaskext.oldext_simple' - - -def test_flaskext_old_package_import_normal(oldext_package): - from flask.ext.oldext_package import ext_id - assert ext_id == 'oldext_package' - - -def test_flaskext_old_package_import_module(oldext_package): - from flask.ext import oldext_package - assert oldext_package.ext_id == 'oldext_package' - assert oldext_package.__name__ == 'flaskext.oldext_package' - - -def test_flaskext_old_package_import_submodule(oldext_package): - from flask.ext.oldext_package import submodule - assert submodule.__name__ == 'flaskext.oldext_package.submodule' - assert submodule.test_function() == 42 - - -def test_flaskext_old_package_import_submodule_function(oldext_package): - from flask.ext.oldext_package.submodule import test_function - assert test_function() == 42 - - -def test_flaskext_broken_package_no_module_caching(flaskext_broken): - for x in range(2): - with pytest.raises(ImportError): - import flask.ext.broken - - -def test_no_error_swallowing(flaskext_broken): - with pytest.raises(ImportError) as excinfo: - import flask.ext.broken - # python3.6 raises a subclass of ImportError: 'ModuleNotFoundError' - assert issubclass(excinfo.type, ImportError) - if PY2: - message = 'No module named missing_module' - else: - message = 'No module named \'missing_module\'' - assert str(excinfo.value) == message - assert excinfo.tb.tb_frame.f_globals is globals() - - # reraise() adds a second frame so we need to skip that one too. - # On PY3 we even have another one :( - next = excinfo.tb.tb_next.tb_next - if not PY2: - next = next.tb_next - - import os.path - assert os.path.join('flask_broken', '__init__.py') in \ - next.tb_frame.f_code.co_filename From e0cd80c6bff0d5ca3720f2f8dcac21274827caa5 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 26 Jun 2017 09:22:03 -0700 Subject: [PATCH 309/399] remove old upgrade script --- scripts/flask-07-upgrade.py | 298 ------------------------------------ 1 file changed, 298 deletions(-) delete mode 100644 scripts/flask-07-upgrade.py diff --git a/scripts/flask-07-upgrade.py b/scripts/flask-07-upgrade.py deleted file mode 100644 index 18e1a14b..00000000 --- a/scripts/flask-07-upgrade.py +++ /dev/null @@ -1,298 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" - flask-07-upgrade - ~~~~~~~~~~~~~~~~ - - This command line script scans a whole application tree and attempts to - output a unified diff with all the changes that are necessary to easily - upgrade the application to 0.7 and to not yield deprecation warnings. - - This will also attempt to find `after_request` functions that don't modify - the response and appear to be better suited for `teardown_request`. - - This application is indeed an incredible hack, but because what it - attempts to accomplish is impossible to do statically it tries to support - the most common patterns at least. The diff it generates should be - hand reviewed and not applied blindly without making backups. - - :copyright: (c) Copyright 2015 by Armin Ronacher. - :license: see LICENSE for more details. -""" -from __future__ import print_function -import re -import os -import inspect -import difflib -import posixpath -from optparse import OptionParser - -try: - import ast -except ImportError: - ast = None - - -TEMPLATE_LOOKAHEAD = 4096 - -_app_re_part = r'((?:[a-zA-Z_][a-zA-Z0-9_]*app)|app|application)' -_string_re_part = r"('([^'\\]*(?:\\.[^'\\]*)*)'" \ - r'|"([^"\\]*(?:\\.[^"\\]*)*)")' - -_from_import_re = re.compile(r'^\s*from flask import\s+') -_url_for_re = re.compile(r'\b(url_for\()(%s)' % _string_re_part) -_render_template_re = re.compile(r'\b(render_template\()(%s)' % _string_re_part) -_after_request_re = re.compile(r'((?:@\S+\.(?:app_)?))(after_request)(\b\s*$)(?m)') -_module_constructor_re = re.compile(r'([a-zA-Z0-9_][a-zA-Z0-9_]*)\s*=\s*Module' - r'\(__name__\s*(?:,\s*(?:name\s*=\s*)?(%s))?' % - _string_re_part) -_error_handler_re = re.compile(r'%s\.error_handlers\[\s*(\d+)\s*\]' % _app_re_part) -_mod_route_re = re.compile(r'@([a-zA-Z0-9_][a-zA-Z0-9_]*)\.route') -_blueprint_related = [ - (re.compile(r'request\.module'), 'request.blueprint'), - (re.compile(r'register_module'), 'register_blueprint'), - (re.compile(r'%s\.modules' % _app_re_part), '\\1.blueprints') -] - - -def make_diff(filename, old, new): - for line in difflib.unified_diff(old.splitlines(), new.splitlines(), - posixpath.normpath(posixpath.join('a', filename)), - posixpath.normpath(posixpath.join('b', filename)), - lineterm=''): - print(line) - - -def looks_like_teardown_function(node): - returns = [x for x in ast.walk(node) if isinstance(x, ast.Return)] - if len(returns) != 1: - return - return_def = returns[0] - resp_name = node.args.args[0] - if not isinstance(return_def.value, ast.Name) or \ - return_def.value.id != resp_name.id: - return - - for body_node in node.body: - for child in ast.walk(body_node): - if isinstance(child, ast.Name) and \ - child.id == resp_name.id: - if child is not return_def.value: - return - - return resp_name.id - - -def fix_url_for(contents, module_declarations=None): - if module_declarations is None: - skip_module_test = True - else: - skip_module_test = False - mapping = dict(module_declarations) - annotated_lines = [] - - def make_line_annotations(): - if not annotated_lines: - last_index = 0 - for line in contents.splitlines(True): - last_index += len(line) - annotated_lines.append((last_index, line)) - - def backtrack_module_name(call_start): - make_line_annotations() - for idx, (line_end, line) in enumerate(annotated_lines): - if line_end > call_start: - for _, line in reversed(annotated_lines[:idx]): - match = _mod_route_re.search(line) - if match is not None: - shortname = match.group(1) - return mapping.get(shortname) - - def handle_match(match): - if not skip_module_test: - modname = backtrack_module_name(match.start()) - if modname is None: - return match.group(0) - prefix = match.group(1) - endpoint = ast.literal_eval(match.group(2)) - if endpoint.startswith('.'): - endpoint = endpoint[1:] - elif '.' not in endpoint: - endpoint = '.' + endpoint - else: - return match.group(0) - return prefix + repr(endpoint) - return _url_for_re.sub(handle_match, contents) - - -def fix_teardown_funcs(contents): - - def is_return_line(line): - args = line.strip().split() - return args and args[0] == 'return' - - def fix_single(match, lines, lineno): - if not lines[lineno + 1].startswith('def'): - return - block_lines = inspect.getblock(lines[lineno + 1:]) - func_code = ''.join(block_lines) - if func_code[0].isspace(): - node = ast.parse('if 1:\n' + func_code).body[0].body - else: - node = ast.parse(func_code).body[0] - response_param_name = looks_like_teardown_function(node) - if response_param_name is None: - return - before = lines[:lineno] - decorator = [match.group(1) + - match.group(2).replace('after_', 'teardown_') + - match.group(3)] - body = [line.replace(response_param_name, 'exception') - for line in block_lines if - not is_return_line(line)] - after = lines[lineno + len(block_lines) + 1:] - return before + decorator + body + after - - content_lines = contents.splitlines(True) - while 1: - found_one = False - for idx, line in enumerate(content_lines): - match = _after_request_re.match(line) - if match is None: - continue - new_content_lines = fix_single(match, content_lines, idx) - if new_content_lines is not None: - content_lines = new_content_lines - break - else: - break - - return ''.join(content_lines) - - -def get_module_autoname(filename): - directory, filename = os.path.split(filename) - if filename != '__init__.py': - return os.path.splitext(filename)[0] - return os.path.basename(directory) - - -def rewrite_from_imports(prefix, fromlist, lineiter): - import_block = [prefix, fromlist] - if fromlist[0] == '(' and fromlist[-1] != ')': - for line in lineiter: - import_block.append(line) - if line.rstrip().endswith(')'): - break - elif fromlist[-1] == '\\': - for line in lineiter: - import_block.append(line) - if line.rstrip().endswith('\\'): - break - - return ''.join(import_block).replace('Module', 'Blueprint') - - -def rewrite_blueprint_imports(contents): - new_file = [] - lineiter = iter(contents.splitlines(True)) - for line in lineiter: - match = _from_import_re.search(line) - if match is not None: - new_file.extend(rewrite_from_imports(match.group(), - line[match.end():], - lineiter)) - else: - new_file.append(line) - return ''.join(new_file) - - -def rewrite_for_blueprints(contents, filename): - modules_declared = [] - def handle_match(match): - target = match.group(1) - name_param = match.group(2) - if name_param is None: - modname = get_module_autoname(filename) - else: - modname = ast.literal_eval(name_param) - modules_declared.append((target, modname)) - return '%s = %s' % (target, 'Blueprint(%r, __name__' % modname) - new_contents = _module_constructor_re.sub(handle_match, contents) - - if modules_declared: - new_contents = rewrite_blueprint_imports(new_contents) - - for pattern, replacement in _blueprint_related: - new_contents = pattern.sub(replacement, new_contents) - return new_contents, dict(modules_declared) - - -def upgrade_python_file(filename, contents, teardown): - new_contents = contents - if teardown: - new_contents = fix_teardown_funcs(new_contents) - new_contents, modules = rewrite_for_blueprints(new_contents, filename) - new_contents = fix_url_for(new_contents, modules) - new_contents = _error_handler_re.sub('\\1.error_handler_spec[None][\\2]', - new_contents) - make_diff(filename, contents, new_contents) - - -def upgrade_template_file(filename, contents): - new_contents = fix_url_for(contents, None) - make_diff(filename, contents, new_contents) - - -def walk_path(path): - this_file = os.path.realpath(__file__).rstrip('c') - for dirpath, dirnames, filenames in os.walk(path): - dirnames[:] = [x for x in dirnames if not x.startswith('.')] - for filename in filenames: - filename = os.path.join(dirpath, filename) - if os.path.realpath(filename) == this_file: - continue - if filename.endswith('.py'): - yield filename, 'python' - # skip files that are diffs. These might be false positives - # when run multiple times. - elif not filename.endswith(('.diff', '.patch', '.udiff')): - with open(filename) as f: - contents = f.read(TEMPLATE_LOOKAHEAD) - if '{% for' or '{% if' or '{{ url_for' in contents: - yield filename, 'template' - - -def scan_path(path=None, teardown=True): - for filename, type in walk_path(path): - with open(filename) as f: - contents = f.read() - if type == 'python': - upgrade_python_file(filename, contents, teardown) - elif type == 'template': - upgrade_template_file(filename, contents) - - -def main(): - """Entrypoint""" - parser = OptionParser(usage='%prog [options] [paths]') - parser.add_option('-T', '--no-teardown-detection', dest='no_teardown', - action='store_true', help='Do not attempt to ' - 'detect teardown function rewrites.') - parser.add_option('-b', '--bundled-templates', dest='bundled_tmpl', - action='store_true', help='Indicate to the system ' - 'that templates are bundled with modules. Default ' - 'is auto detect.') - options, args = parser.parse_args() - if not args: - args = ['.'] - - if ast is None: - parser.error('Python 2.6 or later is required to run the upgrade script.') - - for path in args: - scan_path(path, teardown=not options.no_teardown) - - -if __name__ == '__main__': - main() From 5bc0d15359b73afcb324eb696f9caeb362efa7cc Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 26 Jun 2017 09:34:26 -0700 Subject: [PATCH 310/399] un-deprecate request.json --- CHANGES | 3 +++ flask/wrappers.py | 8 -------- tests/test_deprecations.py | 26 -------------------------- 3 files changed, 3 insertions(+), 34 deletions(-) delete mode 100644 tests/test_deprecations.py diff --git a/CHANGES b/CHANGES index 7280e469..207da344 100644 --- a/CHANGES +++ b/CHANGES @@ -96,6 +96,9 @@ Major release, unreleased - ``Flask.static_path`` - use ``Flask.static_url_path`` instead. - ``Request.module`` - use ``Request.blueprint`` instead. +- The ``request.json`` property is no longer deprecated. (`#1421`_) + +.. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 .. _#1898: https://github.com/pallets/flask/pull/1898 diff --git a/flask/wrappers.py b/flask/wrappers.py index 4dde5f3c..8c559c0e 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -8,8 +8,6 @@ :copyright: (c) 2015 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ -from warnings import warn - from werkzeug.exceptions import BadRequest from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase @@ -44,13 +42,7 @@ class JSONMixin(object): """This will contain the parsed JSON data if the mimetype indicates JSON (:mimetype:`application/json`, see :meth:`is_json`), otherwise it will be ``None``. - - .. deprecated:: 1.0 - Use :meth:`get_json` instead. """ - warn(DeprecationWarning( - "'json' is deprecated. Use 'get_json()' instead." - ), stacklevel=2) return self.get_json() def _get_data_for_json(self, cache): diff --git a/tests/test_deprecations.py b/tests/test_deprecations.py deleted file mode 100644 index 5dfff127..00000000 --- a/tests/test_deprecations.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -""" - tests.deprecations - ~~~~~~~~~~~~~~~~~~ - - Tests deprecation support. Not used currently. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import flask - - -class TestRequestDeprecation(object): - def test_request_json(self, recwarn, app, client): - """Request.json is deprecated""" - - @app.route('/', methods=['POST']) - def index(): - assert flask.request.json == {'spam': 42} - print(flask.request.json) - return 'OK' - - client.post('/', data='{"spam": 42}', content_type='application/json') - recwarn.pop(DeprecationWarning) From 4f30cb7c57c4b5d74ba98953f59670a647623f7a Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Tue, 27 Jun 2017 07:27:10 -0700 Subject: [PATCH 311/399] fix variable substitution in error message --- flask/cli.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 536f53ee..ddf3c323 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -203,9 +203,10 @@ def locate_app(script_info, app_id, raise_if_not_found=True): ) elif raise_if_not_found: raise NoAppException( - 'The file/path provided (%s) does not appear to exist. Please' - ' verify the path is correct. If app is not on PYTHONPATH,' - ' ensure the extension is .py.'.format(module=module) + 'The file/path provided ({module}) does not appear to exist.' + ' Please verify the path is correct. If app is not on' + ' PYTHONPATH, ensure the extension is .py.'.format( + module=module) ) else: return From 465922e5f1cb76cf669a38afe25facde2176b9f0 Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 28 Jun 2017 07:58:06 -0700 Subject: [PATCH 312/399] clean up secret key docs consistent key across docs and examples consistent key across tests, set in conftest --- docs/api.rst | 12 ++++++------ docs/config.rst | 8 ++++---- docs/patterns/flashing.rst | 2 +- docs/quickstart.rst | 23 ++++++++++------------- docs/tutorial/setup.rst | 8 ++++---- examples/flaskr/flaskr/factory.py | 2 +- examples/minitwit/minitwit/minitwit.py | 2 +- flask/app.py | 4 ++-- tests/conftest.py | 7 +------ tests/static/config.json | 2 +- tests/test_basic.py | 24 ++---------------------- tests/test_config.py | 13 +++++++------ tests/test_signals.py | 5 +---- tests/test_templating.py | 2 -- tests/test_testing.py | 6 ------ 15 files changed, 41 insertions(+), 79 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 421f3de5..4173b7bb 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -103,12 +103,12 @@ Response Objects Sessions -------- -If you have the :attr:`Flask.secret_key` set you can use sessions in Flask -applications. A session basically makes it possible to remember -information from one request to another. The way Flask does this is by -using a signed cookie. So the user can look at the session contents, but -not modify it unless they know the secret key, so make sure to set that -to something complex and unguessable. +If you have set :attr:`Flask.secret_key` (or configured it from +:data:`SECRET_KEY`) you can use sessions in Flask applications. A session makes +it possible to remember information from one request to another. The way Flask +does this is by using a signed cookie. The user can look at the session +contents, but can't modify it unless they know the secret key, so make sure to +set that to something complex and unguessable. To access the current session you can use the :class:`session` object: diff --git a/docs/config.rst b/docs/config.rst index 7280fae2..b0b896d3 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -39,7 +39,7 @@ method:: app.config.update( DEBUG=True, - SECRET_KEY='...' + SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/' ) .. admonition:: Debug Mode with the ``flask`` Script @@ -367,7 +367,7 @@ Here is an example of a configuration file:: # Example configuration DEBUG = False - SECRET_KEY = '?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' + SECRET_KEY = b'_5#y2L"F4Q8z\n\xec]/' Make sure to load the configuration very early on, so that extensions have the ability to access the configuration when starting up. There are other @@ -385,7 +385,7 @@ from the environment. Environment variables can be set on Linux or OS X with the export command in the shell before starting the server:: - $ export SECRET_KEY='?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' + $ export SECRET_KEY='5f352379324c22463451387a0aec5d2f' $ export DEBUG=False $ python run-app.py * Running on http://127.0.0.1:5000/ @@ -393,7 +393,7 @@ the shell before starting the server:: On Windows systems use the `set` builtin instead:: - >set SECRET_KEY='?\xbf,\xb4\x8d\xa3"<\x9c\xb0@\x0f5\xab,w\xee\x8d$0\x13\x8b83' + >set SECRET_KEY='5f352379324c22463451387a0aec5d2f' >set DEBUG=False While this approach is straightforward to use, it is important to remember that diff --git a/docs/patterns/flashing.rst b/docs/patterns/flashing.rst index 7efd1446..a61c719f 100644 --- a/docs/patterns/flashing.rst +++ b/docs/patterns/flashing.rst @@ -22,7 +22,7 @@ So here is a full example:: request, url_for app = Flask(__name__) - app.secret_key = 'some_secret' + app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' @app.route('/') def index(): diff --git a/docs/quickstart.rst b/docs/quickstart.rst index d56fa8e2..7440ccd2 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -160,7 +160,7 @@ Screenshot of the debugger in action: :class: screenshot :alt: screenshot of debugger in action -More information on using the debugger can be found in the `Werkzeug +More information on using the debugger can be found in the `Werkzeug documentation`_. .. _Werkzeug documentation: http://werkzeug.pocoo.org/docs/debug/#using-the-debugger @@ -724,6 +724,9 @@ sessions work:: app = Flask(__name__) + # Set the secret key to some random bytes. Keep this really secret! + app.secret_key = b'_5#y2L"F4Q8z\n\xec]/' + @app.route('/') def index(): if 'username' in session: @@ -748,24 +751,18 @@ sessions work:: session.pop('username', None) return redirect(url_for('index')) - # set the secret key. keep this really secret: - app.secret_key = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT' - The :func:`~flask.escape` mentioned here does escaping for you if you are not using the template engine (as in this example). .. admonition:: How to generate good secret keys - The problem with random is that it's hard to judge what is truly random. And - a secret key should be as random as possible. Your operating system - has ways to generate pretty random stuff based on a cryptographic - random generator which can be used to get such a key:: - - >>> import os - >>> os.urandom(24) - '\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O Date: Wed, 28 Jun 2017 08:02:51 -0700 Subject: [PATCH 313/399] one more secret key in docs --- docs/config.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index b0b896d3..223bd6a8 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -121,7 +121,8 @@ The following configuration values are used internally by Flask: application. It should be a long random string of bytes, although unicode is accepted too. For example, copy the output of this to your config:: - python -c 'import os; print(os.urandom(32))' + python -c 'import os; print(os.urandom(16))' + b'_5#y2L"F4Q8z\n\xec]/' **Do not reveal the secret key when posting questions or committing code.** From 6162ecef19823310c316a1a287d3c3abda48b2aa Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 28 Jun 2017 08:06:18 -0700 Subject: [PATCH 314/399] detox breaks docs test env for some reason [ci skip] --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index cb6dd342..a38b8ebe 100644 --- a/tox.ini +++ b/tox.ini @@ -66,5 +66,5 @@ commands = skip_install = true deps = detox commands = - detox -e py{36,35,34,33,27,26,py},py{36,27,py}-simplejson,py{36,33,27,26,py}-devel,py{36,33,27,26,py}-lowest,docs-html + detox -e py{36,35,34,33,27,26,py},py{36,27,py}-simplejson,py{36,33,27,26,py}-devel,py{36,33,27,26,py}-lowest tox -e coverage-report From 8207b1563e1878d8943471d74c9b311bcb195bf7 Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 28 Jun 2017 19:55:39 -0700 Subject: [PATCH 315/399] fix formatting -R is no longer an egg_info option closes #2400 [ci skip] --- docs/patterns/distribute.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/patterns/distribute.rst b/docs/patterns/distribute.rst index f4a07579..693f3731 100644 --- a/docs/patterns/distribute.rst +++ b/docs/patterns/distribute.rst @@ -88,8 +88,8 @@ support them and they make debugging a lot harder. Tagging Builds -------------- -It is useful to distinguish between release and development builds. Add a -:file:`setup.cfg` file to configure these options. +It is useful to distinguish between release and development builds. Add a +:file:`setup.cfg` file to configure these options. :: [egg_info] tag_build = .dev From 5909e26fba86351063bd622cedf6a4c25eba2e79 Mon Sep 17 00:00:00 2001 From: William Horton Date: Fri, 30 Jun 2017 11:28:48 -0400 Subject: [PATCH 316/399] Remove unused import from test_basic (#2403) --- tests/test_basic.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_basic.py b/tests/test_basic.py index 7c39c45f..a46bfbfc 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -9,7 +9,6 @@ :license: BSD, see LICENSE for more details. """ -import pickle import re import time import uuid From a417e41d27cc14a0fe74dc49840b8921aeed70b6 Mon Sep 17 00:00:00 2001 From: Bijan Vakili Date: Fri, 7 Jul 2017 17:34:44 -0700 Subject: [PATCH 317/399] Update documentation and regression tests to clarify that Flask.teardown_appcontext() only receives unhandled exceptions --- flask/app.py | 6 +++-- tests/test_appctx.py | 53 +++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/flask/app.py b/flask/app.py index fb791f33..63faa5e2 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1484,8 +1484,10 @@ class Flask(_PackageBoundObject): Since a request context typically also manages an application context it would also be called when you pop a request context. - When a teardown function was called because of an exception it will - be passed an error object. + When a teardown function was called because of an unhandled exception + it will be passed an error object. Note that if a :meth:`errorhandler` + is registered, it will handle the exception and the teardown will not + receive it. The return values of teardown functions are ignored. diff --git a/tests/test_appctx.py b/tests/test_appctx.py index 7ef7b479..bf3474e9 100644 --- a/tests/test_appctx.py +++ b/tests/test_appctx.py @@ -81,7 +81,7 @@ def test_app_tearing_down_with_previous_exception(app): assert cleanup_stuff == [None] -def test_app_tearing_down_with_handled_exception(app): +def test_app_tearing_down_with_handled_exception_by_except_block(app): cleanup_stuff = [] @app.teardown_appcontext @@ -97,6 +97,57 @@ def test_app_tearing_down_with_handled_exception(app): assert cleanup_stuff == [None] +def test_app_tearing_down_with_handled_exception_by_app_handler(app): + cleanup_stuff = [] + + class AppConfig(): + PROPAGATE_EXCEPTIONS = True + app.config.from_object(AppConfig()) + + @app.teardown_appcontext + def cleanup(exception): + cleanup_stuff.append(exception) + + @app.route('/') + def index(): + raise Exception('dummy') + + @app.errorhandler(Exception) + def handler(f): + return flask.jsonify(str(f)) + + test_client = app.test_client() + with app.app_context(): + test_client.get('/') + + assert cleanup_stuff == [None] + + +def test_app_tearing_down_with_unhandled_exception(app): + cleanup_stuff = [] + + class AppConfig(): + PROPAGATE_EXCEPTIONS = True + app.config.from_object(AppConfig()) + + @app.teardown_appcontext + def cleanup(exception): + cleanup_stuff.append(exception) + + @app.route('/') + def index(): + raise Exception('dummy') + + test_client = app.test_client() + with pytest.raises(Exception): + with app.app_context(): + test_client.get('/') + + assert len(cleanup_stuff) == 1 + assert isinstance(cleanup_stuff[0], Exception) + assert str(cleanup_stuff[0]) == 'dummy' + + def test_app_ctx_globals_methods(app, app_ctx): # get assert flask.g.get('foo') is None From 9560f22bb65d7c06d10ea3dfe3d7a81a73f68af2 Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 10 Jul 2017 07:10:47 -0700 Subject: [PATCH 318/399] clean up --- flask/app.py | 4 ++-- tests/test_appctx.py | 20 ++++++-------------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/flask/app.py b/flask/app.py index 63faa5e2..9dfc3783 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1485,8 +1485,8 @@ class Flask(_PackageBoundObject): context it would also be called when you pop a request context. When a teardown function was called because of an unhandled exception - it will be passed an error object. Note that if a :meth:`errorhandler` - is registered, it will handle the exception and the teardown will not + it will be passed an error object. If an :meth:`errorhandler` is + registered, it will handle the exception and the teardown will not receive it. The return values of teardown functions are ignored. diff --git a/tests/test_appctx.py b/tests/test_appctx.py index bf3474e9..fc2f6b13 100644 --- a/tests/test_appctx.py +++ b/tests/test_appctx.py @@ -97,13 +97,10 @@ def test_app_tearing_down_with_handled_exception_by_except_block(app): assert cleanup_stuff == [None] -def test_app_tearing_down_with_handled_exception_by_app_handler(app): +def test_app_tearing_down_with_handled_exception_by_app_handler(app, client): + app.config['PROPAGATE_EXCEPTIONS'] = True cleanup_stuff = [] - class AppConfig(): - PROPAGATE_EXCEPTIONS = True - app.config.from_object(AppConfig()) - @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) @@ -116,20 +113,16 @@ def test_app_tearing_down_with_handled_exception_by_app_handler(app): def handler(f): return flask.jsonify(str(f)) - test_client = app.test_client() with app.app_context(): - test_client.get('/') + client.get('/') assert cleanup_stuff == [None] -def test_app_tearing_down_with_unhandled_exception(app): +def test_app_tearing_down_with_unhandled_exception(app, client): + app.config['PROPAGATE_EXCEPTIONS'] = True cleanup_stuff = [] - class AppConfig(): - PROPAGATE_EXCEPTIONS = True - app.config.from_object(AppConfig()) - @app.teardown_appcontext def cleanup(exception): cleanup_stuff.append(exception) @@ -138,10 +131,9 @@ def test_app_tearing_down_with_unhandled_exception(app): def index(): raise Exception('dummy') - test_client = app.test_client() with pytest.raises(Exception): with app.app_context(): - test_client.get('/') + client.get('/') assert len(cleanup_stuff) == 1 assert isinstance(cleanup_stuff[0], Exception) From 59f7966e3192bcf478a213ba0aa9cd3bda7da4e4 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 13 Jul 2017 08:42:53 -0700 Subject: [PATCH 319/399] support passing environ to test client (#2412) closes #2411 --- CHANGES | 3 +++ flask/testing.py | 40 +++++++++++++++++++++++++++++++--------- tests/test_testing.py | 18 ++++++++++++++++++ 3 files changed, 52 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 207da344..cc763166 100644 --- a/CHANGES +++ b/CHANGES @@ -97,6 +97,8 @@ Major release, unreleased - ``Request.module`` - use ``Request.blueprint`` instead. - The ``request.json`` property is no longer deprecated. (`#1421`_) +- Support passing an existing ``EnvironBuilder`` or ``dict`` to + ``test_client.open``. (`#2412`_) .. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1489: https://github.com/pallets/flask/pull/1489 @@ -124,6 +126,7 @@ Major release, unreleased .. _#2374: https://github.com/pallets/flask/pull/2374 .. _#2373: https://github.com/pallets/flask/pull/2373 .. _#2385: https://github.com/pallets/flask/issues/2385 +.. _#2412: https://github.com/pallets/flask/pull/2412 Version 0.12.2 -------------- diff --git a/flask/testing.py b/flask/testing.py index f73454af..d5109ce1 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -144,19 +144,41 @@ class FlaskClient(Client): self.cookie_jar.extract_wsgi(c.request.environ, headers) def open(self, *args, **kwargs): - kwargs.setdefault('environ_overrides', {}) \ - ['flask._preserve_context'] = self.preserve_context - kwargs.setdefault('environ_base', self.environ_base) - as_tuple = kwargs.pop('as_tuple', False) buffered = kwargs.pop('buffered', False) follow_redirects = kwargs.pop('follow_redirects', False) - builder = make_test_environ_builder(self.application, *args, **kwargs) - return Client.open(self, builder, - as_tuple=as_tuple, - buffered=buffered, - follow_redirects=follow_redirects) + if ( + not kwargs and len(args) == 1 + and isinstance(args[0], (EnvironBuilder, dict)) + ): + environ = self.environ_base.copy() + + if isinstance(args[0], EnvironBuilder): + environ.update(args[0].get_environ()) + else: + environ.update(args[0]) + + environ['flask._preserve_context'] = self.preserve_context + else: + kwargs.setdefault('environ_overrides', {}) \ + ['flask._preserve_context'] = self.preserve_context + kwargs.setdefault('environ_base', self.environ_base) + builder = make_test_environ_builder( + self.application, *args, **kwargs + ) + + try: + environ = builder.get_environ() + finally: + builder.close() + + return Client.open( + self, environ, + as_tuple=as_tuple, + buffered=buffered, + follow_redirects=follow_redirects + ) def __enter__(self): if self.preserve_context: diff --git a/tests/test_testing.py b/tests/test_testing.py index 068eb209..e94da483 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -15,6 +15,7 @@ import werkzeug from flask._compat import text_type from flask.json import jsonify +from flask.testing import make_test_environ_builder def test_environ_defaults_from_config(app, client): @@ -74,6 +75,23 @@ def test_environ_base_modified(app, client, app_ctx): assert flask.g.user_agent == 'Bar' +def test_client_open_environ(app, client, request): + @app.route('/index') + def index(): + return flask.request.remote_addr + + builder = make_test_environ_builder(app, path='/index', method='GET') + request.addfinalizer(builder.close) + + rv = client.open(builder) + assert rv.data == b'127.0.0.1' + + environ = builder.get_environ() + client.environ_base['REMOTE_ADDR'] = '127.0.0.2' + rv = client.open(environ) + assert rv.data == b'127.0.0.2' + + def test_specify_url_scheme(app, client): @app.route('/') def index(): From fb845b90328278f4a557f898024a53800752fd53 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 14 Jul 2017 19:27:45 -0700 Subject: [PATCH 320/399] allow local packages in FLASK_APP don't require .py extension in FLASK_APP add tests for nested package loading parametrize cli loading tests --- CHANGES | 3 + flask/cli.py | 129 ++++++------- tests/test_apps/cliapp/factory.py | 8 +- tests/test_apps/cliapp/inner1/__init__.py | 3 + .../cliapp/inner1/inner2/__init__.py | 0 tests/test_apps/cliapp/inner1/inner2/flask.py | 3 + tests/test_apps/cliapp/message.txt | 1 + tests/test_cli.py | 170 ++++++++++-------- 8 files changed, 162 insertions(+), 155 deletions(-) create mode 100644 tests/test_apps/cliapp/inner1/__init__.py create mode 100644 tests/test_apps/cliapp/inner1/inner2/__init__.py create mode 100644 tests/test_apps/cliapp/inner1/inner2/flask.py create mode 100644 tests/test_apps/cliapp/message.txt diff --git a/CHANGES b/CHANGES index cc763166..dc11d5e4 100644 --- a/CHANGES +++ b/CHANGES @@ -52,6 +52,8 @@ Major release, unreleased - FLASK_APP=myproject.app:create_app('dev') support. - ``FLASK_APP`` can be set to an app factory, with arguments if needed, for example ``FLASK_APP=myproject.app:create_app('dev')``. (`#2326`_) +- ``FLASK_APP`` can point to local packages that are not installed in dev mode, + although `pip install -e` should still be preferred. (`#2414`_) - ``View.provide_automatic_options = True`` is set on the view function from ``View.as_view``, to be detected in ``app.add_url_rule``. (`#2316`_) - Error handling will try handlers registered for ``blueprint, code``, @@ -127,6 +129,7 @@ Major release, unreleased .. _#2373: https://github.com/pallets/flask/pull/2373 .. _#2385: https://github.com/pallets/flask/issues/2385 .. _#2412: https://github.com/pallets/flask/pull/2412 +.. _#2414: https://github.com/pallets/flask/pull/2414 Version 0.12.2 -------------- diff --git a/flask/cli.py b/flask/cli.py index ddf3c323..3568c10f 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -130,7 +130,7 @@ def find_app_by_string(string, script_info, module): if isinstance(app, Flask): return app else: - raise RuntimeError('Failed to find application in module ' + raise NoAppException('Failed to find application in module ' '"{name}"'.format(name=module)) except TypeError as e: new_error = NoAppException( @@ -147,85 +147,61 @@ def find_app_by_string(string, script_info, module): 'or function expression.'.format(string=string)) -def prepare_exec_for_file(filename): +def prepare_import(path): """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 = [] + path = os.path.realpath(path) - # Chop off file extensions or package markers - if os.path.split(filename)[1] == '__init__.py': - filename = os.path.dirname(filename) - elif filename.endswith('.py'): - filename = filename[:-3] - 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 - while 1: - dirpath, extra = os.path.split(dirpath) - module.append(extra) - if not os.path.isfile(os.path.join(dirpath, '__init__.py')): + if os.path.splitext(path)[1] == '.py': + path = os.path.splitext(path)[0] + + if os.path.basename(path) == '__init__': + path = os.path.dirname(path) + + module_name = [] + + # move up until outside package structure (no __init__.py) + while True: + path, name = os.path.split(path) + module_name.append(name) + + if not os.path.exists(os.path.join(path, '__init__.py')): break - if sys.path[0] != dirpath: - sys.path.insert(0, dirpath) + if sys.path[0] != path: + sys.path.insert(0, path) - return '.'.join(module[::-1]) + return '.'.join(module_name[::-1]) -def locate_app(script_info, app_id, raise_if_not_found=True): +def locate_app(script_info, module_name, app_name, raise_if_not_found=True): """Attempts to locate the application.""" __traceback_hide__ = True - if ':' in app_id: - module, app_obj = app_id.split(':', 1) - else: - module = app_id - app_obj = None - try: - __import__(module) + __import__(module_name) except ImportError: # Reraise the ImportError if it occurred within the imported module. # Determine this by checking whether the trace has a depth > 1. if sys.exc_info()[-1].tb_next: - stack_trace = traceback.format_exc() raise NoAppException( - 'There was an error trying to import the app ({module}):\n' - '{stack_trace}'.format( - module=module, stack_trace=stack_trace - ) + 'While importing "{name}", an ImportError was raised:' + '\n\n{tb}'.format(name=module_name, tb=traceback.format_exc()) ) elif raise_if_not_found: raise NoAppException( - 'The file/path provided ({module}) does not appear to exist.' - ' Please verify the path is correct. If app is not on' - ' PYTHONPATH, ensure the extension is .py.'.format( - module=module) + 'Could not import "{name}"."'.format(name=module_name) ) else: return - mod = sys.modules[module] + module = sys.modules[module_name] - if app_obj is None: - return find_best_app(script_info, mod) + if app_name is None: + return find_best_app(script_info, module) else: - return find_app_by_string(app_obj, script_info, mod) - - -def find_default_import_path(): - app = os.environ.get('FLASK_APP') - if app is None: - return - if os.path.isfile(app): - return prepare_exec_for_file(app) - return app + return find_app_by_string(app_name, script_info, module) def get_version(ctx, param, value): @@ -308,15 +284,8 @@ class ScriptInfo(object): """ def __init__(self, app_import_path=None, create_app=None): - if create_app is None: - if app_import_path is None: - app_import_path = find_default_import_path() - self.app_import_path = app_import_path - else: - app_import_path = None - #: Optionally the import path for the Flask application. - self.app_import_path = app_import_path + self.app_import_path = app_import_path or os.environ.get('FLASK_APP') #: Optionally a function that is passed the script info to create #: the instance of the application. self.create_app = create_app @@ -335,37 +304,39 @@ class ScriptInfo(object): if self._loaded_app is not None: return self._loaded_app + app = None + if self.create_app is not None: - rv = call_factory(self.create_app, self) + app = call_factory(self.create_app, self) else: if self.app_import_path: - rv = locate_app(self, self.app_import_path) + path, name = (self.app_import_path.split(':', 1) + [None])[:2] + import_name = prepare_import(path) + app = locate_app(self, import_name, name) else: - for module in ['wsgi.py', 'app.py']: - import_path = prepare_exec_for_file(module) - rv = locate_app( - self, import_path, raise_if_not_found=False + for path in ('wsgi.py', 'app.py'): + import_name = prepare_import(path) + app = locate_app( + self, import_name, None, raise_if_not_found=False ) - if rv: + if app: break - if not rv: - raise NoAppException( - 'Could not locate Flask application. You did not provide ' - 'the FLASK_APP environment variable, and a wsgi.py or ' - 'app.py module was not found in the current directory.\n\n' - 'For more information see ' - 'http://flask.pocoo.org/docs/latest/quickstart/' - ) + if not app: + raise NoAppException( + 'Could not locate a Flask application. You did not provide ' + 'the "FLASK_APP" environment variable, and a "wsgi.py" or ' + '"app.py" module was not found in the current directory.' + ) debug = get_debug_flag() if debug is not None: - rv._reconfigure_for_run_debug(debug) + app._reconfigure_for_run_debug(debug) - self._loaded_app = rv - return rv + self._loaded_app = app + return app pass_script_info = click.make_pass_decorator(ScriptInfo, ensure=True) diff --git a/tests/test_apps/cliapp/factory.py b/tests/test_apps/cliapp/factory.py index b0d4771e..2e8598e8 100644 --- a/tests/test_apps/cliapp/factory.py +++ b/tests/test_apps/cliapp/factory.py @@ -4,12 +4,12 @@ from flask import Flask def create_app(): - return Flask('create_app') + return Flask('app') def create_app2(foo, bar): - return Flask("_".join(['create_app2', foo, bar])) + return Flask('_'.join(['app2', foo, bar])) -def create_app3(foo, bar, script_info): - return Flask("_".join(['create_app3', foo, bar])) +def create_app3(foo, script_info): + return Flask('_'.join(['app3', foo, script_info.data['test']])) diff --git a/tests/test_apps/cliapp/inner1/__init__.py b/tests/test_apps/cliapp/inner1/__init__.py new file mode 100644 index 00000000..8330f6e0 --- /dev/null +++ b/tests/test_apps/cliapp/inner1/__init__.py @@ -0,0 +1,3 @@ +from flask import Flask + +application = Flask(__name__) diff --git a/tests/test_apps/cliapp/inner1/inner2/__init__.py b/tests/test_apps/cliapp/inner1/inner2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/test_apps/cliapp/inner1/inner2/flask.py b/tests/test_apps/cliapp/inner1/inner2/flask.py new file mode 100644 index 00000000..d7562aac --- /dev/null +++ b/tests/test_apps/cliapp/inner1/inner2/flask.py @@ -0,0 +1,3 @@ +from flask import Flask + +app = Flask(__name__) diff --git a/tests/test_apps/cliapp/message.txt b/tests/test_apps/cliapp/message.txt new file mode 100644 index 00000000..fc2b2cf0 --- /dev/null +++ b/tests/test_apps/cliapp/message.txt @@ -0,0 +1 @@ +So long, and thanks for all the fish. diff --git a/tests/test_cli.py b/tests/test_cli.py index 0e0a56ad..5fba5229 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -11,7 +11,8 @@ # the Revised BSD License. # Copyright (C) 2015 CERN. # -from __future__ import absolute_import, print_function +from __future__ import absolute_import + import os import sys from functools import partial @@ -19,11 +20,10 @@ from functools import partial import click import pytest from click.testing import CliRunner -from flask import Flask, current_app -from flask.cli import cli, AppGroup, FlaskGroup, NoAppException, ScriptInfo, \ - find_best_app, locate_app, with_appcontext, prepare_exec_for_file, \ - find_default_import_path, get_version +from flask import Flask, current_app +from flask.cli import AppGroup, FlaskGroup, NoAppException, ScriptInfo, \ + find_best_app, get_version, locate_app, prepare_import, with_appcontext @pytest.fixture @@ -125,78 +125,104 @@ def test_find_best_app(test_apps): pytest.raises(NoAppException, find_best_app, script_info, Module) -def test_prepare_exec_for_file(test_apps): - """Expect the correct path to be set and the correct module name to be returned. - - :func:`prepare_exec_for_file` has a side effect, where - the parent directory of given file is added to `sys.path`. +cwd = os.getcwd() +test_path = os.path.abspath(os.path.join( + os.path.dirname(__file__), 'test_apps' +)) + + +@pytest.mark.parametrize('value,path,result', ( + ('test', cwd, 'test'), + ('test.py', cwd, 'test'), + ('a/test', os.path.join(cwd, 'a'), 'test'), + ('test/__init__.py', cwd, 'test'), + ('test/__init__', cwd, 'test'), + # nested package + ( + os.path.join(test_path, 'cliapp', 'inner1', '__init__'), + test_path, 'cliapp.inner1' + ), + ( + os.path.join(test_path, 'cliapp', 'inner1', 'inner2'), + test_path, 'cliapp.inner1.inner2' + ), + # dotted name + ('test.a.b', cwd, 'test.a.b'), + (os.path.join(test_path, 'cliapp.app'), test_path, 'cliapp.app'), + # not a Python file, will be caught during import + ( + os.path.join(test_path, 'cliapp', 'message.txt'), + test_path, 'cliapp.message.txt' + ), +)) +def test_prepare_import(request, value, path, result): + """Expect the correct path to be set and the correct import and app names + to be returned. + + :func:`prepare_exec_for_file` has a side effect where the parent directory + of the given import is added to :data:`sys.path`. This is reset after the + test runs. """ - realpath = os.path.realpath('/tmp/share/test.py') - dirname = os.path.dirname(realpath) - assert prepare_exec_for_file('/tmp/share/test.py') == 'test' - assert dirname in sys.path - - realpath = os.path.realpath('/tmp/share/__init__.py') - dirname = os.path.dirname(os.path.dirname(realpath)) - assert prepare_exec_for_file('/tmp/share/__init__.py') == 'share' - assert dirname in sys.path + original_path = sys.path[:] + + def reset_path(): + sys.path[:] = original_path + + request.addfinalizer(reset_path) + + assert prepare_import(value) == result + assert sys.path[0] == path + + +@pytest.mark.parametrize('iname,aname,result', ( + ('cliapp.app', None, 'testapp'), + ('cliapp.app', 'testapp', 'testapp'), + ('cliapp.factory', None, 'app'), + ('cliapp.factory', 'create_app', 'app'), + ('cliapp.factory', 'create_app()', 'app'), + # no script_info + ('cliapp.factory', 'create_app2("foo", "bar")', 'app2_foo_bar'), + # trailing comma space + ('cliapp.factory', 'create_app2("foo", "bar", )', 'app2_foo_bar'), + # takes script_info + ('cliapp.factory', 'create_app3("foo")', 'app3_foo_spam'), +)) +def test_locate_app(test_apps, iname, aname, result): + info = ScriptInfo() + info.data['test'] = 'spam' + assert locate_app(info, iname, aname).name == result + + +@pytest.mark.parametrize('iname,aname', ( + ('notanapp.py', None), + ('cliapp/app', None), + ('cliapp.app', 'notanapp'), + # not enough arguments + ('cliapp.factory', 'create_app2("foo")'), + # nested import error + ('cliapp.importerrorapp', None), + # not a Python file + ('cliapp.message.txt', None), + # space before arg list + ('cliapp.factory', 'create_app ()'), +)) +def test_locate_app_raises(test_apps, iname, aname): + info = ScriptInfo() with pytest.raises(NoAppException): - prepare_exec_for_file('/tmp/share/test.txt') + locate_app(info, iname, aname) -def test_locate_app(test_apps): - """Test of locate_app.""" - script_info = ScriptInfo() - assert locate_app(script_info, "cliapp.app").name == "testapp" - assert locate_app(script_info, "cliapp.app:testapp").name == "testapp" - assert locate_app(script_info, "cliapp.factory").name == "create_app" - assert locate_app( - script_info, "cliapp.factory").name == "create_app" - assert locate_app( - script_info, "cliapp.factory:create_app").name == "create_app" - assert locate_app( - script_info, "cliapp.factory:create_app()").name == "create_app" - assert locate_app( - script_info, "cliapp.factory:create_app2('foo', 'bar')" - ).name == "create_app2_foo_bar" - assert locate_app( - script_info, "cliapp.factory:create_app2('foo', 'bar', )" - ).name == "create_app2_foo_bar" - assert locate_app( - script_info, "cliapp.factory:create_app3('baz', 'qux')" - ).name == "create_app3_baz_qux" - assert locate_app(script_info, "cliapp.multiapp:app1").name == "app1" - pytest.raises( - NoAppException, locate_app, script_info, "notanpp.py") - pytest.raises( - NoAppException, locate_app, script_info, "cliapp/app") - pytest.raises( - RuntimeError, locate_app, script_info, "cliapp.app:notanapp") - pytest.raises( - NoAppException, locate_app, - script_info, "cliapp.factory:create_app2('foo')") - pytest.raises( - NoAppException, locate_app, - script_info, "cliapp.factory:create_app ()") - pytest.raises( - NoAppException, locate_app, script_info, "cliapp.importerrorapp") - assert locate_app( - script_info, "notanpp.py", raise_if_not_found=False - ) is None - - -def test_find_default_import_path(test_apps, monkeypatch, tmpdir): - """Test of find_default_import_path.""" - monkeypatch.delitem(os.environ, 'FLASK_APP', raising=False) - assert find_default_import_path() == None - monkeypatch.setitem(os.environ, 'FLASK_APP', 'notanapp') - assert find_default_import_path() == 'notanapp' - tmpfile = tmpdir.join('testapp.py') - tmpfile.write('') - monkeypatch.setitem(os.environ, 'FLASK_APP', str(tmpfile)) - expect_rv = prepare_exec_for_file(str(tmpfile)) - assert find_default_import_path() == expect_rv +def test_locate_app_suppress_raise(): + info = ScriptInfo() + app = locate_app(info, 'notanapp.py', None, raise_if_not_found=False) + assert app is None + + # only direct import error is suppressed + with pytest.raises(NoAppException): + locate_app( + info, 'cliapp.importerrorapp', None, raise_if_not_found=False + ) def test_get_version(test_apps, capsys): From 8e2ef5bde3a00603cca02e3fef8ffbdb665f5fbe Mon Sep 17 00:00:00 2001 From: asmith26 Date: Sun, 16 Jul 2017 20:07:25 +0100 Subject: [PATCH 321/399] Fix typo in docs (#2419) --- docs/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/security.rst b/docs/security.rst index b8714186..97c93292 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -132,7 +132,7 @@ HTTP Strict Transport Security (HSTS) Tells the browser to convert all HTTP requests to HTTPS, preventing man-in-the-middle (MITM) attacks. :: - response.haders['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' + response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains' - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security From 491d331e6e0a7915c9e6d9b3287688ac7a3f56ca Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 14 Jul 2017 22:37:53 -0700 Subject: [PATCH 322/399] load env vars using python-dotenv --- .gitignore | 2 + .travis.yml | 5 ++ CHANGES | 4 ++ docs/api.rst | 2 + docs/cli.rst | 34 +++++++++++-- docs/installation.rst | 3 ++ flask/app.py | 42 ++++++++++------ flask/cli.py | 101 ++++++++++++++++++++++++++++++++------ setup.py | 2 + tests/test_apps/.env | 3 ++ tests/test_apps/.flaskenv | 3 ++ tests/test_cli.py | 73 ++++++++++++++++++++++++--- tox.ini | 3 +- 13 files changed, 235 insertions(+), 42 deletions(-) create mode 100644 tests/test_apps/.env create mode 100644 tests/test_apps/.flaskenv diff --git a/.gitignore b/.gitignore index fb9baf35..231c0873 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ .DS_Store +.env +.flaskenv *.pyc *.pyo env diff --git a/.travis.yml b/.travis.yml index 9984bc3c..ed690253 100644 --- a/.travis.yml +++ b/.travis.yml @@ -33,6 +33,11 @@ script: cache: - pip +branches: + only: + - master + - /^.*-maintenance$/ + notifications: email: false irc: diff --git a/CHANGES b/CHANGES index dc11d5e4..a282617e 100644 --- a/CHANGES +++ b/CHANGES @@ -101,6 +101,9 @@ Major release, unreleased - The ``request.json`` property is no longer deprecated. (`#1421`_) - Support passing an existing ``EnvironBuilder`` or ``dict`` to ``test_client.open``. (`#2412`_) +- The ``flask`` command and ``app.run`` will load environment variables using + from ``.env`` and ``.flaskenv`` files if python-dotenv is installed. + (`#2416`_) .. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1489: https://github.com/pallets/flask/pull/1489 @@ -130,6 +133,7 @@ Major release, unreleased .. _#2385: https://github.com/pallets/flask/issues/2385 .. _#2412: https://github.com/pallets/flask/pull/2412 .. _#2414: https://github.com/pallets/flask/pull/2414 +.. _#2416: https://github.com/pallets/flask/pull/2416 Version 0.12.2 -------------- diff --git a/docs/api.rst b/docs/api.rst index 4173b7bb..e24160c4 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -814,6 +814,8 @@ Command Line Interface .. autoclass:: ScriptInfo :members: +.. autofunction:: load_dotenv + .. autofunction:: with_appcontext .. autofunction:: pass_script_info diff --git a/docs/cli.rst b/docs/cli.rst index 52885f43..b481991d 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -97,9 +97,8 @@ 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:: +easily. For instance if you want a shell command to initialize the database you +can do this:: import click from flask import Flask @@ -134,6 +133,35 @@ decorator:: def example(): pass + +.. _dotenv: + +Loading Environment Variables From ``.env`` Files +------------------------------------------------- + +If `python-dotenv`_ is installed, running the :command:`flask` command will set +environment variables defined in the files :file:`.env` and :file:`.flaskenv`. +This can be used to avoid having to set ``FLASK_APP`` manually every time you +open a new terminal, and to set configuration using environment variables +similar to how some deployment services work. + +Variables set on the command line are used over those set in :file:`.env`, +which are used over those set in :file:`.flaskenv`. :file:`.flaskenv` should be +used for public variables, such as ``FLASK_APP``, while :file:`.env` should not +be committed to your repository so that it can set private variables. + +Directories are scanned upwards from the directory you call :command:`flask` +from to locate the files. The current working directory will be set to the +location of the file, with the assumption that that is the top level project +directory. + +The files are only loaded by the :command:`flask` command or calling +:meth:`~flask.Flask.run`. If you would like to load these files when running in +production, you should call :func:`~flask.cli.load_dotenv` manually. + +.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + Factory Functions ----------------- diff --git a/docs/installation.rst b/docs/installation.rst index cd869b9a..0ae05f06 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -41,9 +41,12 @@ use them if you install them. * `SimpleJSON`_ is a fast JSON implementation that is compatible with Python's ``json`` module. It is preferred for JSON operations if it is installed. +* `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask`` + commands. .. _Blinker: https://pythonhosted.org/blinker/ .. _SimpleJSON: https://simplejson.readthedocs.io/ +.. _python-dotenv: https://github.com/theskumar/python-dotenv#readme Virtual environments -------------------- diff --git a/flask/app.py b/flask/app.py index 9dfc3783..afa5fd1c 100644 --- a/flask/app.py +++ b/flask/app.py @@ -820,7 +820,9 @@ class Flask(_PackageBoundObject): self.debug = debug self.jinja_env.auto_reload = self.templates_auto_reload - def run(self, host=None, port=None, debug=None, **options): + def run( + self, host=None, port=None, debug=None, load_dotenv=True, **options + ): """Runs the application on a local development server. Do not use ``run()`` in a production setting. It is not intended to @@ -849,30 +851,40 @@ class Flask(_PackageBoundObject): won't catch any exceptions because there won't be any to catch. + :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to + have the server available externally as well. Defaults to + ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config variable + if present. + :param port: the port of the webserver. Defaults to ``5000`` or the + port defined in the ``SERVER_NAME`` config variable if present. + :param debug: if given, enable or disable debug mode. See + :attr:`debug`. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + :param options: the options to be forwarded to the underlying Werkzeug + server. See :func:`werkzeug.serving.run_simple` for more + information. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment + variables from :file:`.env` and :file:`.flaskenv` files. + .. versionchanged:: 0.10 The default port is now picked from the ``SERVER_NAME`` variable. - :param host: the hostname to listen on. Set this to ``'0.0.0.0'`` to - have the server available externally as well. Defaults to - ``'127.0.0.1'`` or the host in the ``SERVER_NAME`` config - variable if present. - :param port: the port of the webserver. Defaults to ``5000`` or the - port defined in the ``SERVER_NAME`` config variable if - present. - :param debug: if given, enable or disable debug mode. - See :attr:`debug`. - :param options: the options to be forwarded to the underlying - Werkzeug server. See - :func:`werkzeug.serving.run_simple` for more - information. """ # Change this into a no-op if the server is invoked from the # command line. Have a look at cli.py for more information. - if os.environ.get('FLASK_RUN_FROM_CLI_SERVER') == '1': + if os.environ.get('FLASK_RUN_FROM_CLI') == 'true': from .debughelpers import explain_ignored_app_run explain_ignored_app_run() return + if load_dotenv: + from flask.cli import load_dotenv + load_dotenv() + if debug is not None: self._reconfigure_for_run_debug(bool(debug)) diff --git a/flask/cli.py b/flask/cli.py index 3568c10f..bea4a29f 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -8,6 +8,7 @@ :copyright: (c) 2015 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ +from __future__ import print_function import ast import inspect @@ -22,10 +23,14 @@ from threading import Lock, Thread import click from . import __version__ -from ._compat import iteritems, reraise +from ._compat import getargspec, iteritems, reraise from .globals import current_app from .helpers import get_debug_flag -from ._compat import getargspec + +try: + import dotenv +except ImportError: + dotenv = None class NoAppException(click.UsageError): @@ -394,14 +399,23 @@ class FlaskGroup(AppGroup): 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. + shell commands wil be added. :param add_version_option: adds the ``--version`` option. - :param create_app: an optional callback that is passed the script info - and returns the loaded app. + :param create_app: an optional callback that is passed the script info and + returns the loaded app. + :param load_dotenv: Load the nearest :file:`.env` and :file:`.flaskenv` + files to set environment variables. Will also change the working + directory to the directory containing the first file found. + + .. versionchanged:: 1.0 + If installed, python-dotenv will be used to load environment variables + from :file:`.env` and :file:`.flaskenv` files. """ - def __init__(self, add_default_commands=True, create_app=None, - add_version_option=True, **extra): + def __init__( + self, add_default_commands=True, create_app=None, + add_version_option=True, load_dotenv=True, **extra + ): params = list(extra.pop('params', None) or ()) if add_version_option: @@ -409,6 +423,7 @@ class FlaskGroup(AppGroup): AppGroup.__init__(self, params=params, **extra) self.create_app = create_app + self.load_dotenv = load_dotenv if add_default_commands: self.add_command(run_command) @@ -472,12 +487,75 @@ class FlaskGroup(AppGroup): return sorted(rv) def main(self, *args, **kwargs): + # Set a global flag that indicates that we were invoked from the + # command line interface. This is detected by Flask.run to make the + # call into a no-op. This is necessary to avoid ugly errors when the + # script that is loaded here also attempts to start a server. + os.environ['FLASK_RUN_FROM_CLI'] = 'true' + + if self.load_dotenv: + load_dotenv() + 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 AppGroup.main(self, *args, **kwargs) + return super(FlaskGroup, self).main(*args, **kwargs) + + +def _path_is_ancestor(path, other): + """Take ``other`` and remove the length of ``path`` from it. Then join it + to ``path``. If it is the original value, ``path`` is an ancestor of + ``other``.""" + return os.path.join(path, other[len(path):].lstrip(os.sep)) == other + + +def load_dotenv(path=None): + """Load "dotenv" files in order of precedence to set environment variables. + + If an env var is already set it is not overwritten, so earlier files in the + list are preferred over later files. + + Changes the current working directory to the location of the first file + found, with the assumption that it is in the top level project directory + and will be where the Python path should import local packages from. + + This is a no-op if `python-dotenv`_ is not installed. + + .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme + + :param path: Load the file at this location instead of searching. + :return: ``True`` if a file was loaded. + + .. versionadded:: 1.0 + """ + + if dotenv is None: + return + + if path is not None: + return dotenv.load_dotenv(path) + + new_dir = None + + for name in ('.env', '.flaskenv'): + path = dotenv.find_dotenv(name, usecwd=True) + + if not path: + continue + + if new_dir is None: + new_dir = os.path.dirname(path) + + dotenv.load_dotenv(path) + + if new_dir and os.getcwd() != new_dir: + os.chdir(new_dir) + + return new_dir is not None # at least one file was located and loaded @click.command('run', short_help='Runs a development server.') @@ -512,13 +590,6 @@ def run_command(info, host, port, reload, debugger, eager_loading, """ from werkzeug.serving import run_simple - # Set a global flag that indicates that we were invoked from the - # command line interface provided server command. This is detected - # by Flask.run to make the call into a no-op. This is necessary to - # avoid ugly errors when the script that is loaded here also attempts - # to start a server. - os.environ['FLASK_RUN_FROM_CLI_SERVER'] = '1' - debug = get_debug_flag() if reload is None: reload = bool(debug) diff --git a/setup.py b/setup.py index acade846..ff2f3107 100644 --- a/setup.py +++ b/setup.py @@ -75,8 +75,10 @@ setup( 'click>=4.0', ], extras_require={ + 'dotenv': ['python-dotenv'], 'dev': [ 'blinker', + 'python-dotenv', 'greenlet', 'pytest>=3', 'coverage', diff --git a/tests/test_apps/.env b/tests/test_apps/.env new file mode 100644 index 00000000..13ac3483 --- /dev/null +++ b/tests/test_apps/.env @@ -0,0 +1,3 @@ +FOO=env +SPAM=1 +EGGS=2 diff --git a/tests/test_apps/.flaskenv b/tests/test_apps/.flaskenv new file mode 100644 index 00000000..59f96af7 --- /dev/null +++ b/tests/test_apps/.flaskenv @@ -0,0 +1,3 @@ +FOO=flaskenv +BAR=bar +EGGS=0 diff --git a/tests/test_cli.py b/tests/test_cli.py index 5fba5229..c66bd17e 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -19,11 +19,31 @@ from functools import partial import click import pytest +from _pytest.monkeypatch import notset from click.testing import CliRunner from flask import Flask, current_app -from flask.cli import AppGroup, FlaskGroup, NoAppException, ScriptInfo, \ - find_best_app, get_version, locate_app, prepare_import, with_appcontext +from flask.cli import AppGroup, FlaskGroup, NoAppException, ScriptInfo, dotenv, \ + find_best_app, get_version, load_dotenv, locate_app, prepare_import, \ + with_appcontext + +cwd = os.getcwd() +test_path = os.path.abspath(os.path.join( + os.path.dirname(__file__), 'test_apps' +)) + + +@pytest.fixture(autouse=True) +def manage_os_environ(monkeypatch): + # can't use monkeypatch.delitem since we don't want to restore a value + os.environ.pop('FLASK_APP', None) + os.environ.pop('FLASK_DEBUG', None) + # use monkeypatch internals to force-delete environ keys + monkeypatch._setitem.extend(( + (os.environ, 'FLASK_APP', notset), + (os.environ, 'FLASK_DEBUG', notset), + (os.environ, 'FLASK_RUN_FROM_CLI', notset), + )) @pytest.fixture @@ -125,12 +145,6 @@ def test_find_best_app(test_apps): pytest.raises(NoAppException, find_best_app, script_info, Module) -cwd = os.getcwd() -test_path = os.path.abspath(os.path.join( - os.path.dirname(__file__), 'test_apps' -)) - - @pytest.mark.parametrize('value,path,result', ( ('test', cwd, 'test'), ('test.py', cwd, 'test'), @@ -414,3 +428,46 @@ class TestRoutes: assert 'GET, HEAD, OPTIONS, POST' not in output output = invoke(['routes', '--all-methods']).output assert 'GET, HEAD, OPTIONS, POST' in output + + +need_dotenv = pytest.mark.skipif( + dotenv is None, reason='dotenv is not installed' +) + + +@need_dotenv +def test_load_dotenv(monkeypatch): + # can't use monkeypatch.delitem since the keys don't exist yet + for item in ('FOO', 'BAR', 'SPAM'): + monkeypatch._setitem.append((os.environ, item, notset)) + + monkeypatch.setenv('EGGS', '3') + monkeypatch.chdir(os.path.join(test_path, 'cliapp', 'inner1')) + load_dotenv() + assert os.getcwd() == test_path + # .flaskenv doesn't overwrite .env + assert os.environ['FOO'] == 'env' + # set only in .flaskenv + assert os.environ['BAR'] == 'bar' + # set only in .env + assert os.environ['SPAM'] == '1' + # set manually, files don't overwrite + assert os.environ['EGGS'] == '3' + + +@need_dotenv +def test_dotenv_path(monkeypatch): + for item in ('FOO', 'BAR', 'EGGS'): + monkeypatch._setitem.append((os.environ, item, notset)) + + cwd = os.getcwd() + load_dotenv(os.path.join(test_path, '.flaskenv')) + assert os.getcwd() == cwd + assert 'FOO' in os.environ + + +def test_dotenv_optional(monkeypatch): + monkeypatch.setattr('flask.cli.dotenv', None) + monkeypatch.chdir(test_path) + load_dotenv() + assert 'FOO' not in os.environ diff --git a/tox.ini b/tox.ini index a38b8ebe..ff4d7999 100644 --- a/tox.ini +++ b/tox.ini @@ -15,6 +15,7 @@ deps = coverage greenlet blinker + python-dotenv lowest: Werkzeug==0.9 lowest: Jinja2==2.4 @@ -67,4 +68,4 @@ skip_install = true deps = detox commands = detox -e py{36,35,34,33,27,26,py},py{36,27,py}-simplejson,py{36,33,27,26,py}-devel,py{36,33,27,26,py}-lowest - tox -e coverage-report + tox -e docs-html,coverage-report From 241673fd153bad011c3fbbb41580450f39874cad Mon Sep 17 00:00:00 2001 From: Igor Kasianov Date: Thu, 27 Jul 2017 14:44:23 +0300 Subject: [PATCH 323/399] make_test_environ_builder: use url_scheme from path if provided When providing https url in path ("https://example.com/") we hope that we will get https scheme in environment --- flask/testing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/testing.py b/flask/testing.py index d5109ce1..e0b7bcf0 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -40,10 +40,10 @@ def make_test_environ_builder( if subdomain: http_host = '{0}.{1}'.format(subdomain, http_host) + url = url_parse(path) if url_scheme is None: - url_scheme = app.config['PREFERRED_URL_SCHEME'] + url_scheme = url.scheme or app.config['PREFERRED_URL_SCHEME'] - url = url_parse(path) base_url = '{0}://{1}/{2}'.format( url_scheme, url.netloc or http_host, app_root.lstrip('/') ) From 417bc336de7350f91378183e88cb35c1ef8c75c5 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 27 Jul 2017 07:32:03 -0700 Subject: [PATCH 324/399] egg_info -R flag is gone --- docs/patterns/distribute.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/distribute.rst b/docs/patterns/distribute.rst index 693f3731..0c7c8658 100644 --- a/docs/patterns/distribute.rst +++ b/docs/patterns/distribute.rst @@ -96,7 +96,7 @@ It is useful to distinguish between release and development builds. Add a tag_date = 1 [aliases] - release = egg_info -RDb '' + release = egg_info -Db '' Running ``python setup.py sdist`` will create a development package with ".dev" and the current date appended: ``flaskr-1.0.dev20160314.tar.gz``. From a89bdb3395b22c963456d1fcdebbd77c5c6f618c Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 29 Jul 2017 13:03:08 -0700 Subject: [PATCH 325/399] prefer the url's scheme over the kwarg tabs -> spaces add test add changelog --- CHANGES | 3 +++ flask/testing.py | 10 ++++++---- tests/test_testing.py | 8 ++++++++ 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/CHANGES b/CHANGES index a282617e..b44334db 100644 --- a/CHANGES +++ b/CHANGES @@ -104,6 +104,8 @@ Major release, unreleased - The ``flask`` command and ``app.run`` will load environment variables using from ``.env`` and ``.flaskenv`` files if python-dotenv is installed. (`#2416`_) +- When passing a full URL to the test client, use the scheme in the URL instead + of the ``PREFERRED_URL_SCHEME``. (`#2436`_) .. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1489: https://github.com/pallets/flask/pull/1489 @@ -134,6 +136,7 @@ Major release, unreleased .. _#2412: https://github.com/pallets/flask/pull/2412 .. _#2414: https://github.com/pallets/flask/pull/2414 .. _#2416: https://github.com/pallets/flask/pull/2416 +.. _#2436: https://github.com/pallets/flask/pull/2436 Version 0.12.2 -------------- diff --git a/flask/testing.py b/flask/testing.py index e0b7bcf0..f29c6b17 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -40,12 +40,14 @@ def make_test_environ_builder( if subdomain: http_host = '{0}.{1}'.format(subdomain, http_host) - url = url_parse(path) if url_scheme is None: - url_scheme = url.scheme or app.config['PREFERRED_URL_SCHEME'] + url_scheme = app.config['PREFERRED_URL_SCHEME'] - base_url = '{0}://{1}/{2}'.format( - url_scheme, url.netloc or http_host, app_root.lstrip('/') + url = url_parse(path) + base_url = '{scheme}://{netloc}/{path}'.format( + scheme=url.scheme or url_scheme, + netloc=url.netloc or http_host, + path=app_root.lstrip('/') ) path = url.path diff --git a/tests/test_testing.py b/tests/test_testing.py index e94da483..c25a0305 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -104,6 +104,14 @@ def test_specify_url_scheme(app, client): assert rv.data == b'https://localhost/' +def test_path_is_url(app): + eb = make_test_environ_builder(app, 'https://example.com/') + assert eb.url_scheme == 'https' + assert eb.host == 'example.com' + assert eb.script_root == '' + assert eb.path == '/' + + def test_blueprint_with_subdomain(app, client): app.config['SERVER_NAME'] = 'example.com:1234' app.config['APPLICATION_ROOT'] = '/foo' From 66b1b752da1ccc67c1073d4a99c4141a1b2be445 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 28 Jul 2017 14:55:52 -0700 Subject: [PATCH 326/399] simplify logging configuration single default handler and formatter don't remove handlers configure level once using setLevel document logging reorganize logging tests --- CHANGES | 6 ++ docs/config.rst | 20 +--- docs/contents.rst.inc | 1 + docs/errorhandling.rst | 212 +------------------------------------- docs/logging.rst | 175 +++++++++++++++++++++++++++++++ flask/app.py | 56 ++++------ flask/logging.py | 125 +++++++++------------- tests/test_basic.py | 33 +++++- tests/test_helpers.py | 104 ++----------------- tests/test_logging.py | 91 ++++++++++++++++ tests/test_subclassing.py | 12 +-- tests/test_templating.py | 24 ++--- tests/test_testing.py | 1 - 13 files changed, 404 insertions(+), 456 deletions(-) create mode 100644 docs/logging.rst create mode 100644 tests/test_logging.py diff --git a/CHANGES b/CHANGES index b44334db..fe48808f 100644 --- a/CHANGES +++ b/CHANGES @@ -106,6 +106,12 @@ Major release, unreleased (`#2416`_) - When passing a full URL to the test client, use the scheme in the URL instead of the ``PREFERRED_URL_SCHEME``. (`#2436`_) +- ``app.logger`` has been simplified. ``LOGGER_NAME`` and + ``LOGGER_HANDLER_POLICY`` config was removed. The logger is always named + ``flask.app``. The level is only set on first access, it doesn't check + ``app.debug`` each time. Only one format is used, not different ones + depending on ``app.debug``. No handlers are removed, and a handler is only + added if no handlers are already configured. (`#2436`_) .. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1489: https://github.com/pallets/flask/pull/1489 diff --git a/docs/config.rst b/docs/config.rst index 223bd6a8..cf119cba 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -199,21 +199,6 @@ The following configuration values are used internally by Flask: Default: ``timedelta(hours=12)`` (``43200`` seconds) -.. py:data:: LOGGER_NAME - - The name of the logger that the Flask application sets up. If not set, - it will take the import name passed to ``Flask.__init__``. - - Default: ``None`` - -.. py:data:: LOGGER_HANDLER_POLICY - - When to activate the application's logger handler. ``'always'`` always - enables it, ``'debug'`` only activates it in debug mode, ``'production'`` - only activates it when not in debug mode, and ``'never'`` never enables it. - - Default: ``'always'`` - .. py:data:: SERVER_NAME Inform the application what host and port it is bound to. Required for @@ -329,6 +314,11 @@ The following configuration values are used internally by Flask: ``SESSION_REFRESH_EACH_REQUEST``, ``TEMPLATES_AUTO_RELOAD``, ``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING`` +.. versionchanged:: 1.0 + + ``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See + :ref:`logging` for information about configuration. + Configuring from Files ---------------------- diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc index e77f7b60..de4d7a91 100644 --- a/docs/contents.rst.inc +++ b/docs/contents.rst.inc @@ -16,6 +16,7 @@ instructions for web development with Flask. templating testing errorhandling + logging config signals views diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst index 84c649ce..413680dd 100644 --- a/docs/errorhandling.rst +++ b/docs/errorhandling.rst @@ -143,213 +143,11 @@ determined. Handlers are prioritized by specificity of the exception classes they are registered for instead of the order they are registered in. -Error Mails ------------ - -If the application runs in production mode (which it will do on your -server) you might not see any log messages. The reason for that is that -Flask by default will just report to the WSGI error stream or stderr -(depending on what's available). Where this ends up is sometimes hard to -find. Often it's in your webserver's log files. - -I can pretty much promise you however that if you only use a logfile for -the application errors you will never look at it except for debugging an -issue when a user reported it for you. What you probably want instead is -a mail the second the exception happened. Then you get an alert and you -can do something about it. - -Flask uses the Python builtin logging system, and it can actually send -you mails for errors which is probably what you want. Here is how you can -configure the Flask logger to send you mails for exceptions:: - - ADMINS = ['yourname@example.com'] - if not app.debug: - import logging - from logging.handlers import SMTPHandler - mail_handler = SMTPHandler('127.0.0.1', - 'server-error@example.com', - ADMINS, 'YourApplication Failed') - mail_handler.setLevel(logging.ERROR) - app.logger.addHandler(mail_handler) - -So what just happened? We created a new -:class:`~logging.handlers.SMTPHandler` that will send mails with the mail -server listening on ``127.0.0.1`` to all the `ADMINS` from the address -*server-error@example.com* with the subject "YourApplication Failed". If -your mail server requires credentials, these can also be provided. For -that check out the documentation for the -:class:`~logging.handlers.SMTPHandler`. - -We also tell the handler to only send errors and more critical messages. -Because we certainly don't want to get a mail for warnings or other -useless logs that might happen during request handling. - -Before you run that in production, please also look at :ref:`logformat` to -put more information into that error mail. That will save you from a lot -of frustration. - - -Logging to a File ------------------ - -Even if you get mails, you probably also want to log warnings. It's a -good idea to keep as much information around that might be required to -debug a problem. By default as of Flask 0.11, errors are logged to your -webserver's log automatically. Warnings however are not. Please note -that Flask itself will not issue any warnings in the core system, so it's -your responsibility to warn in the code if something seems odd. - -There are a couple of handlers provided by the logging system out of the -box but not all of them are useful for basic error logging. The most -interesting are probably the following: - -- :class:`~logging.FileHandler` - logs messages to a file on the - filesystem. -- :class:`~logging.handlers.RotatingFileHandler` - logs messages to a file - on the filesystem and will rotate after a certain number of messages. -- :class:`~logging.handlers.NTEventLogHandler` - will log to the system - event log of a Windows system. If you are deploying on a Windows box, - this is what you want to use. -- :class:`~logging.handlers.SysLogHandler` - sends logs to a UNIX - syslog. - -Once you picked your log handler, do like you did with the SMTP handler -above, just make sure to use a lower setting (I would recommend -`WARNING`):: - - if not app.debug: - import logging - from themodule import TheHandlerYouWant - file_handler = TheHandlerYouWant(...) - file_handler.setLevel(logging.WARNING) - app.logger.addHandler(file_handler) - -.. _logformat: - -Controlling the Log Format --------------------------- - -By default a handler will only write the message string into a file or -send you that message as mail. A log record stores more information, -and it makes a lot of sense to configure your logger to also contain that -information so that you have a better idea of why that error happened, and -more importantly, where it did. - -A formatter can be instantiated with a format string. Note that -tracebacks are appended to the log entry automatically. You don't have to -do that in the log formatter format string. - -Here are some example setups: - -Email -````` - -:: - - from logging import Formatter - mail_handler.setFormatter(Formatter(''' - Message type: %(levelname)s - Location: %(pathname)s:%(lineno)d - Module: %(module)s - Function: %(funcName)s - Time: %(asctime)s - - Message: - - %(message)s - ''')) - -File logging -```````````` - -:: - - from logging import Formatter - file_handler.setFormatter(Formatter( - '%(asctime)s %(levelname)s: %(message)s ' - '[in %(pathname)s:%(lineno)d]' - )) - - -Complex Log Formatting -`````````````````````` - -Here is a list of useful formatting variables for the format string. Note -that this list is not complete, consult the official documentation of the -:mod:`logging` package for a full list. - -.. tabularcolumns:: |p{3cm}|p{12cm}| - -+------------------+----------------------------------------------------+ -| Format | Description | -+==================+====================================================+ -| ``%(levelname)s``| Text logging level for the message | -| | (``'DEBUG'``, ``'INFO'``, ``'WARNING'``, | -| | ``'ERROR'``, ``'CRITICAL'``). | -+------------------+----------------------------------------------------+ -| ``%(pathname)s`` | Full pathname of the source file where the | -| | logging call was issued (if available). | -+------------------+----------------------------------------------------+ -| ``%(filename)s`` | Filename portion of pathname. | -+------------------+----------------------------------------------------+ -| ``%(module)s`` | Module (name portion of filename). | -+------------------+----------------------------------------------------+ -| ``%(funcName)s`` | Name of function containing the logging call. | -+------------------+----------------------------------------------------+ -| ``%(lineno)d`` | Source line number where the logging call was | -| | issued (if available). | -+------------------+----------------------------------------------------+ -| ``%(asctime)s`` | Human-readable time when the | -| | :class:`~logging.LogRecord` was created. | -| | By default this is of the form | -| | ``"2003-07-08 16:49:45,896"`` (the numbers after | -| | the comma are millisecond portion of the time). | -| | This can be changed by subclassing the formatter | -| | and overriding the | -| | :meth:`~logging.Formatter.formatTime` method. | -+------------------+----------------------------------------------------+ -| ``%(message)s`` | The logged message, computed as ``msg % args`` | -+------------------+----------------------------------------------------+ - -If you want to further customize the formatting, you can subclass the -formatter. The formatter has three interesting methods: - -:meth:`~logging.Formatter.format`: - handles the actual formatting. It is passed a - :class:`~logging.LogRecord` object and has to return the formatted - string. -:meth:`~logging.Formatter.formatTime`: - called for `asctime` formatting. If you want a different time format - you can override this method. -:meth:`~logging.Formatter.formatException` - called for exception formatting. It is passed an :attr:`~sys.exc_info` - tuple and has to return a string. The default is usually fine, you - don't have to override it. - -For more information, head over to the official documentation. - - -Other Libraries ---------------- - -So far we only configured the logger your application created itself. -Other libraries might log themselves as well. For example, SQLAlchemy uses -logging heavily in its core. While there is a method to configure all -loggers at once in the :mod:`logging` package, I would not recommend using -it. There might be a situation in which you want to have multiple -separate applications running side by side in the same Python interpreter -and then it becomes impossible to have different logging setups for those. - -Instead, I would recommend figuring out which loggers you are interested -in, getting the loggers with the :func:`~logging.getLogger` function and -iterating over them to attach handlers:: - - from logging import getLogger - loggers = [app.logger, getLogger('sqlalchemy'), - getLogger('otherlibrary')] - for logger in loggers: - logger.addHandler(mail_handler) - logger.addHandler(file_handler) +Logging +------- + +See :ref:`logging` for information on how to log exceptions, such as by +emailing them to admins. Debugging Application Errors diff --git a/docs/logging.rst b/docs/logging.rst new file mode 100644 index 00000000..6bd9266c --- /dev/null +++ b/docs/logging.rst @@ -0,0 +1,175 @@ +.. _logging: + +Logging +======= + +Flask uses standard Python :mod:`logging`. All Flask-related messages are +logged under the ``'flask'`` logger namespace. +:meth:`Flask.logger ` returns the logger named +``'flask.app'``, and can be used to log messages for your application. :: + + @app.route('/login', methods=['POST']) + def login(): + user = get_user(request.form['username']) + + if user.check_password(request.form['password']): + login_user(user) + app.logger.info('%s logged in successfully', user.username) + return redirect(url_for('index')) + else: + app.logger.info('%s failed to log in', user.username) + abort(401) + + +Basic Configuration +------------------- + +When you want to configure logging for your project, you should do it as soon +as possible when the program starts. If :meth:`app.logger ` +is accessed before logging is configured, it will add a default handler. If +possible, configure logging before creating the application object. + +This example uses :func:`~logging.config.dictConfig` to create a logging +configuration similar to Flask's default, except for all logs:: + + from logging.config import dictConfig + + dictConfig({ + 'version': 1, + 'formatters': {'default': { + 'format': '[%(asctime)s] %(levelname)s in %(module)s: %(message)s', + }}, + 'handlers': {'wsgi': { + 'class': 'logging.StreamHandler', + 'stream': 'ext://flask.logging.wsgi_errors_stream', + 'formatter': 'default' + }}, + 'root': { + 'level': 'INFO', + 'handlers': ['wsgi'] + } + }) + + app = Flask(__name__) + + +Default Configuration +````````````````````` + +If you do not configure logging yourself, Flask will add a +:class:`~logging.StreamHandler` to :meth:`app.logger ` +automatically. During requests, it will write to the stream specified by the +WSGI server in ``environ['wsgi.errors']`` (which is usually +:data:`sys.stderr`). Outside a request, it will log to :data:`sys.stderr`. + + +Removing the Default Handler +```````````````````````````` + +If you configured logging after accessing +:meth:`app.logger `, and need to remove the default +handler, you can import and remove it:: + + from flask.logging import default_handler + + app.logger.removeHandler(default_handler) + + +Email Errors to Admins +---------------------- + +When running the application on a remote server for production, you probably +won't be looking at the log messages very often. The WSGI server will probably +send log messages to a file, and you'll only check that file if a user tells +you something went wrong. + +To be proactive about discovering and fixing bugs, you can configure a +:class:`logging.handlers.SMTPHandler` to send an email when errors and higher +are logged. :: + + import logging + from logging.handlers import SMTPHandler + + mail_handler = SMTPHandler( + mailhost='127.0.0.1', + fromaddr='server-error@example.com', + toaddrs=['admin@example.com'], + subject='Application Error' + ) + mail_handler.setLevel(logging.ERROR) + mail_handler.setFormatter(logging.Formatter( + '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' + )) + + if not app.debug: + app.logger.addHandler(mail_handler) + +This requires that you have an SMTP server set up on the same server. See the +Python docs for more information about configuring the handler. + + +Injecting Request Information +----------------------------- + +Seeing more information about the request, such as the IP address, may help +debugging some errors. You can subclass :class:`logging.Formatter` to inject +your own fields that can be used in messages. You can change the formatter for +Flask's default handler, the mail handler defined above, or any other +handler. :: + + from flask import request + from flask.logging import default_handler + + class RequestFormatter(logging.Formatter): + def format(self, record): + record.url = request.url + record.remote_addr = request.remote_addr + return super().format(record) + + formatter = RequestFormatter( + '[%(asctime)s] %(remote_addr)s requested %(url)s\n' + '%(levelname)s in %(module)s: %(message)s' + )) + default_handler.setFormatter(formatter) + mail_handler.setFormatter(formatter) + + +Other Libraries +--------------- + +Other libraries may use logging extensively, and you want to see relevant +messages from those logs too. The simplest way to do this is to add handlers +to the root logger instead of only the app logger. :: + + from flask.logging import default_handler + + root = logging.getLogger() + root.addHandler(default_handler) + root.addHandler(mail_handler) + +Depending on your project, it may be more useful to configure each logger you +care about separately, instead of configuring only the root logger. :: + + for logger in ( + app.logger, + logging.getLogger('sqlalchemy'), + logging.getLogger('other_package'), + ): + logger.addHandler(default_handler) + logger.addHandler(mail_handler) + + +Werkzeug +```````` + +Werkzeug logs basic request/response information to the ``'werkzeug'`` logger. +If the root logger has no handlers configured, Werkzeug adds a +:class:`~logging.StreamHandler` to its logger. + + +Flask Extensions +```````````````` + +Depending on the situation, an extension may choose to log to +:meth:`app.logger ` or its own named logger. Consult each +extension's documentation for details. diff --git a/flask/app.py b/flask/app.py index afa5fd1c..88ca433c 100644 --- a/flask/app.py +++ b/flask/app.py @@ -16,10 +16,9 @@ from functools import update_wrapper from itertools import chain from threading import Lock -from werkzeug.datastructures import ImmutableDict, Headers -from werkzeug.exceptions import BadRequest, HTTPException, \ - InternalServerError, MethodNotAllowed, default_exceptions, \ - BadRequestKeyError +from werkzeug.datastructures import Headers, ImmutableDict +from werkzeug.exceptions import BadRequest, BadRequestKeyError, HTTPException, \ + InternalServerError, MethodNotAllowed, default_exceptions from werkzeug.routing import BuildError, Map, RequestRedirect, Rule from . import cli, json @@ -30,6 +29,7 @@ from .globals import _request_ctx_stack, g, request, session from .helpers import _PackageBoundObject, \ _endpoint_from_view_func, find_package, get_debug_flag, \ get_flashed_messages, locked_cached_property, url_for +from .logging import create_logger from .sessions import SecureCookieSessionInterface from .signals import appcontext_tearing_down, got_request_exception, \ request_finished, request_started, request_tearing_down @@ -37,9 +37,6 @@ from .templating import DispatchingJinjaLoader, Environment, \ _default_template_ctx_processor from .wrappers import Request, Response -# a lock used for logger initialization -_logger_lock = Lock() - # a singleton sentinel value for parameter defaults _sentinel = object() @@ -264,12 +261,6 @@ class Flask(_PackageBoundObject): #: ``USE_X_SENDFILE`` configuration key. Defaults to ``False``. use_x_sendfile = ConfigAttribute('USE_X_SENDFILE') - #: The name of the logger to use. By default the logger name is the - #: package name passed to the constructor. - #: - #: .. versionadded:: 0.4 - logger_name = ConfigAttribute('LOGGER_NAME') - #: The JSON encoder class to use. Defaults to :class:`~flask.json.JSONEncoder`. #: #: .. versionadded:: 0.10 @@ -294,8 +285,6 @@ class Flask(_PackageBoundObject): 'SECRET_KEY': None, 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), 'USE_X_SENDFILE': False, - 'LOGGER_NAME': None, - 'LOGGER_HANDLER_POLICY': 'always', 'SERVER_NAME': None, 'APPLICATION_ROOT': '/', 'SESSION_COOKIE_NAME': 'session', @@ -392,10 +381,6 @@ class Flask(_PackageBoundObject): #: to load a config from files. self.config = self.make_config(instance_relative_config) - # Prepare the deferred setup of the logger. - self._logger = None - self.logger_name = self.import_name - #: A dictionary of all view functions registered. The keys will #: be function names which are also used to generate URLs and #: the values are the function objects themselves. @@ -613,27 +598,28 @@ class Flask(_PackageBoundObject): return rv return self.debug - @property + @locked_cached_property def logger(self): - """A :class:`logging.Logger` object for this application. The - default configuration is to log to stderr if the application is - in debug mode. This logger can be used to (surprise) log messages. - Here some examples:: + """The ``'flask.app'`` logger, a standard Python + :class:`~logging.Logger`. + + In debug mode, the logger's :attr:`~logging.Logger.level` will be set + to :data:`~logging.DEBUG`. - app.logger.debug('A value for debugging') - app.logger.warning('A warning occurred (%d apples)', 42) - app.logger.error('An error occurred') + If there are no handlers configured, a default handler will be added. + See :ref:`logging` for more information. + + .. versionchanged:: 1.0 + Behavior was simplified. The logger is always named + ``flask.app``. The level is only set during configuration, it + doesn't check ``app.debug`` each time. Only one format is used, + not different ones depending on ``app.debug``. No handlers are + removed, and a handler is only added if no handlers are already + configured. .. versionadded:: 0.3 """ - if self._logger and self._logger.name == self.logger_name: - return self._logger - with _logger_lock: - if self._logger and self._logger.name == self.logger_name: - return self._logger - from flask.logging import create_logger - self._logger = rv = create_logger(self) - return rv + return create_logger(self) @locked_cached_property def jinja_env(self): diff --git a/flask/logging.py b/flask/logging.py index 3f888a75..86a3fa33 100644 --- a/flask/logging.py +++ b/flask/logging.py @@ -1,94 +1,69 @@ -# -*- coding: utf-8 -*- -""" - flask.logging - ~~~~~~~~~~~~~ - - Implements the logging support for Flask. - - :copyright: (c) 2015 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - from __future__ import absolute_import +import logging import sys from werkzeug.local import LocalProxy -from logging import getLogger, StreamHandler, Formatter, getLoggerClass, \ - DEBUG, ERROR -from .globals import _request_ctx_stack - -PROD_LOG_FORMAT = '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' -DEBUG_LOG_FORMAT = ( - '-' * 80 + '\n' + - '%(levelname)s in %(module)s [%(pathname)s:%(lineno)d]:\n' + - '%(message)s\n' + - '-' * 80 -) +from .globals import request @LocalProxy -def _proxy_stream(): - """Finds the most appropriate error stream for the application. If a - WSGI request is in flight we log to wsgi.errors, otherwise this resolves - to sys.stderr. +def wsgi_errors_stream(): + """Find the most appropriate error stream for the application. If a request + is active, log to ``wsgi.errors``, otherwise use ``sys.stderr``. + + If you configure your own :class:`logging.StreamHandler`, you may want to + use this for the stream. If you are using file or dict configuration and + can't import this directly, you can refer to it as + ``ext://flask.logging.wsgi_errors_stream``. """ - ctx = _request_ctx_stack.top - if ctx is not None: - return ctx.request.environ['wsgi.errors'] - return sys.stderr + return request.environ['wsgi.errors'] if request else sys.stderr + + +def has_level_handler(logger): + """Check if there is a handler in the logging chain that will handle the + given logger's :meth:`effective level <~logging.Logger.getEffectiveLevel>`. + """ + level = logger.getEffectiveLevel() + current = logger + + while current: + if any(handler.level <= level for handler in current.handlers): + return True + if not current.propagate: + break + + current = current.parent -def _should_log_for(app, mode): - policy = app.config['LOGGER_HANDLER_POLICY'] - if policy == mode or policy == 'always': - return True return False +#: Log messages to :func:`~flask.logging.wsgi_errors_stream` with the format +#: ``[%(asctime)s] %(levelname)s in %(module)s: %(message)s``. +default_handler = logging.StreamHandler(wsgi_errors_stream) +default_handler.setFormatter(logging.Formatter( + '[%(asctime)s] %(levelname)s in %(module)s: %(message)s' +)) + + def create_logger(app): - """Creates a logger for the given application. This logger works - similar to a regular Python logger but changes the effective logging - level based on the application's debug flag. Furthermore this - function also removes all attached handlers in case there was a - logger with the log name before. + """Get the ``'flask.app'`` logger and configure it if needed. + + When :attr:`~flask.Flask.debug` is enabled, set the logger level to + :data:`logging.DEBUG` if it is not set. + + If there is no handler for the logger's effective level, add a + :class:`~logging.StreamHandler` for + :func:`~flask.logging.wsgi_errors_stream` with a basic format. """ - Logger = getLoggerClass() - - class DebugLogger(Logger): - def getEffectiveLevel(self): - if self.level == 0 and app.debug: - return DEBUG - return Logger.getEffectiveLevel(self) - - class DebugHandler(StreamHandler): - def emit(self, record): - if app.debug and _should_log_for(app, 'debug'): - StreamHandler.emit(self, record) - - class ProductionHandler(StreamHandler): - def emit(self, record): - if not app.debug and _should_log_for(app, 'production'): - StreamHandler.emit(self, record) - - debug_handler = DebugHandler() - debug_handler.setLevel(DEBUG) - debug_handler.setFormatter(Formatter(DEBUG_LOG_FORMAT)) - - prod_handler = ProductionHandler(_proxy_stream) - prod_handler.setLevel(ERROR) - prod_handler.setFormatter(Formatter(PROD_LOG_FORMAT)) - - logger = getLogger(app.logger_name) - # just in case that was not a new logger, get rid of all the handlers - # already attached to it. - del logger.handlers[:] - logger.__class__ = DebugLogger - logger.addHandler(debug_handler) - logger.addHandler(prod_handler) - - # Disable propagation by default - logger.propagate = False + logger = logging.getLogger('flask.app') + + if app.debug and logger.level == logging.NOTSET: + logger.setLevel(logging.DEBUG) + + if not has_level_handler(logger): + logger.addHandler(default_handler) return logger diff --git a/tests/test_basic.py b/tests/test_basic.py index a46bfbfc..0e3076df 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -738,7 +738,6 @@ def test_teardown_request_handler_debug_mode(app, client): def test_teardown_request_handler_error(app, client): called = [] - app.config['LOGGER_HANDLER_POLICY'] = 'never' app.testing = False @app.teardown_request @@ -814,7 +813,6 @@ def test_before_after_request_order(app, client): def test_error_handling(app, client): - app.config['LOGGER_HANDLER_POLICY'] = 'never' app.testing = False @app.errorhandler(404) @@ -860,7 +858,6 @@ def test_error_handler_unknown_code(app): def test_error_handling_processing(app, client): - app.config['LOGGER_HANDLER_POLICY'] = 'never' app.testing = False @app.errorhandler(500) @@ -882,7 +879,6 @@ def test_error_handling_processing(app, client): def test_baseexception_error_handling(app, client): - app.config['LOGGER_HANDLER_POLICY'] = 'never' app.testing = False @app.route('/') @@ -1021,6 +1017,34 @@ def test_trapping_of_all_http_exceptions(app, client): client.get('/fail') +def test_error_handler_after_processor_error(app, client): + app.testing = False + + @app.before_request + def before_request(): + if trigger == 'before': + 1 // 0 + + @app.after_request + def after_request(response): + if trigger == 'after': + 1 // 0 + return response + + @app.route('/') + def index(): + return 'Foo' + + @app.errorhandler(500) + def internal_server_error(e): + return 'Hello Server Error', 500 + + for trigger in 'before', 'after': + rv = client.get('/') + assert rv.status_code == 500 + assert rv.data == b'Hello Server Error' + + def test_enctype_debug_helper(app, client): from flask.debughelpers import DebugFilesKeyError app.debug = True @@ -1425,7 +1449,6 @@ def test_test_app_proper_environ(app, client): def test_exception_propagation(app, client): def apprunner(config_key): - app.config['LOGGER_HANDLER_POLICY'] = 'never' @app.route('/') def index(): diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 73c6bee7..cc480c26 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -9,20 +9,19 @@ :license: BSD, see LICENSE for more details. """ -import pytest - +import datetime import os import uuid -import datetime -import flask -from logging import StreamHandler +import pytest from werkzeug.datastructures import Range from werkzeug.exceptions import BadRequest, NotFound -from werkzeug.http import parse_cache_control_header, parse_options_header -from werkzeug.http import http_date +from werkzeug.http import http_date, parse_cache_control_header, \ + parse_options_header + +import flask from flask._compat import StringIO, text_type -from flask.helpers import get_debug_flag, make_response +from flask.helpers import get_debug_flag def has_encoding(name): @@ -660,94 +659,7 @@ class TestSendfile(object): flask.send_from_directory('static', 'bad\x00') -class TestLogging(object): - def test_logger_cache(self): - app = flask.Flask(__name__) - logger1 = app.logger - assert app.logger is logger1 - assert logger1.name == __name__ - app.logger_name = __name__ + '/test_logger_cache' - assert app.logger is not logger1 - - def test_debug_log(self, capsys, app, client): - app.debug = True - - @app.route('/') - def index(): - app.logger.warning('the standard library is dead') - app.logger.debug('this is a debug statement') - return '' - - @app.route('/exc') - def exc(): - 1 // 0 - - with client: - client.get('/') - out, err = capsys.readouterr() - assert 'WARNING in test_helpers [' in err - assert os.path.basename(__file__.rsplit('.', 1)[0] + '.py') in err - assert 'the standard library is dead' in err - assert 'this is a debug statement' in err - - with pytest.raises(ZeroDivisionError): - client.get('/exc') - - def test_debug_log_override(self, app): - app.debug = True - app.logger_name = 'flask_tests/test_debug_log_override' - app.logger.level = 10 - assert app.logger.level == 10 - - def test_exception_logging(self, app, client): - out = StringIO() - app.config['LOGGER_HANDLER_POLICY'] = 'never' - app.logger_name = 'flask_tests/test_exception_logging' - app.logger.addHandler(StreamHandler(out)) - app.testing = False - - @app.route('/') - def index(): - 1 // 0 - - rv = client.get('/') - assert rv.status_code == 500 - assert b'Internal Server Error' in rv.data - - err = out.getvalue() - assert 'Exception on / [GET]' in err - assert 'Traceback (most recent call last):' in err - assert '1 // 0' in err - assert 'ZeroDivisionError:' in err - - def test_processor_exceptions(self, app, client): - app.config['LOGGER_HANDLER_POLICY'] = 'never' - app.testing = False - - @app.before_request - def before_request(): - if trigger == 'before': - 1 // 0 - - @app.after_request - def after_request(response): - if trigger == 'after': - 1 // 0 - return response - - @app.route('/') - def index(): - return 'Foo' - - @app.errorhandler(500) - def internal_server_error(e): - return 'Hello Server Error', 500 - - for trigger in 'before', 'after': - rv = client.get('/') - assert rv.status_code == 500 - assert rv.data == b'Hello Server Error' - +class TestUrlFor(object): def test_url_for_with_anchor(self, app, req_ctx): @app.route('/') diff --git a/tests/test_logging.py b/tests/test_logging.py new file mode 100644 index 00000000..1a010569 --- /dev/null +++ b/tests/test_logging.py @@ -0,0 +1,91 @@ +import logging +import sys + +import pytest + +from flask._compat import StringIO +from flask.logging import default_handler, has_level_handler, \ + wsgi_errors_stream + + +@pytest.fixture(autouse=True) +def reset_logging(monkeypatch): + root_handlers = logging.root.handlers[:] + root_level = logging.root.level + + logger = logging.getLogger('flask.app') + logger.handlers = [] + logger.setLevel(logging.NOTSET) + + yield + + logging.root.handlers[:] = root_handlers + logging.root.setLevel(root_level) + + logger.handlers = [] + logger.setLevel(logging.NOTSET) + + +def test_logger(app): + assert app.logger.name == 'flask.app' + assert app.logger.level == logging.NOTSET + assert app.logger.handlers == [default_handler] + + +def test_logger_debug(app): + app.debug = True + assert app.logger.level == logging.DEBUG + assert app.logger.handlers == [default_handler] + + +def test_existing_handler(app): + logging.root.addHandler(logging.StreamHandler()) + assert app.logger.level == logging.NOTSET + assert not app.logger.handlers + + +def test_wsgi_errors_stream(app, client): + @app.route('/') + def index(): + app.logger.error('test') + return '' + + stream = StringIO() + client.get('/', errors_stream=stream) + assert 'ERROR in test_logging: test' in stream.getvalue() + + assert wsgi_errors_stream._get_current_object() is sys.stderr + + with app.test_request_context(errors_stream=stream): + assert wsgi_errors_stream._get_current_object() is stream + + +def test_has_level_handler(): + logger = logging.getLogger('flask.app') + assert not has_level_handler(logger) + + handler = logging.StreamHandler() + logging.root.addHandler(handler) + assert has_level_handler(logger) + + logger.propagate = False + assert not has_level_handler(logger) + logger.propagate = True + + handler.setLevel(logging.ERROR) + assert not has_level_handler(logger) + + +def test_log_view_exception(app, client): + @app.route('/') + def index(): + raise Exception('test') + + app.testing = False + stream = StringIO() + rv = client.get('/', errors_stream=stream) + assert rv.status_code == 500 + assert rv.data + err = stream.getvalue() + assert 'Exception on / [GET]' in err + assert 'Exception: test' in err diff --git a/tests/test_subclassing.py b/tests/test_subclassing.py index 3f81ecd8..82739a7e 100644 --- a/tests/test_subclassing.py +++ b/tests/test_subclassing.py @@ -9,8 +9,8 @@ :copyright: (c) 2015 by Armin Ronacher. :license: BSD, see LICENSE for more details. """ + import flask -from logging import StreamHandler from flask._compat import StringIO @@ -22,16 +22,12 @@ def test_suppressed_exception_logging(): out = StringIO() app = SuppressedFlask(__name__) - app.logger_name = 'flask_tests/test_suppressed_exception_logging' - app.logger.addHandler(StreamHandler(out)) @app.route('/') def index(): - 1 // 0 + raise Exception('test') - rv = app.test_client().get('/') + rv = app.test_client().get('/', errors_stream=out) assert rv.status_code == 500 assert b'Internal Server Error' in rv.data - - err = out.getvalue() - assert err == '' + assert not out.getvalue() diff --git a/tests/test_templating.py b/tests/test_templating.py index c41bcda8..d871ca4d 100644 --- a/tests/test_templating.py +++ b/tests/test_templating.py @@ -399,7 +399,7 @@ def test_templates_auto_reload_debug_run(app, monkeypatch): assert app.jinja_env.auto_reload == True -def test_template_loader_debugging(test_apps): +def test_template_loader_debugging(test_apps, monkeypatch): from blueprintapp import app called = [] @@ -419,19 +419,15 @@ def test_template_loader_debugging(test_apps): assert 'See http://flask.pocoo.org/docs/blueprints/#templates' in text with app.test_client() as c: - try: - old_load_setting = app.config['EXPLAIN_TEMPLATE_LOADING'] - old_handlers = app.logger.handlers[:] - app.logger.handlers = [_TestHandler()] - app.config['EXPLAIN_TEMPLATE_LOADING'] = True - - with pytest.raises(TemplateNotFound) as excinfo: - c.get('/missing') - - assert 'missing_template.html' in str(excinfo.value) - finally: - app.logger.handlers[:] = old_handlers - app.config['EXPLAIN_TEMPLATE_LOADING'] = old_load_setting + monkeypatch.setitem(app.config, 'EXPLAIN_TEMPLATE_LOADING', True) + monkeypatch.setattr( + logging.getLogger('flask'), 'handlers', [_TestHandler()] + ) + + with pytest.raises(TemplateNotFound) as excinfo: + c.get('/missing') + + assert 'missing_template.html' in str(excinfo.value) assert len(called) == 1 diff --git a/tests/test_testing.py b/tests/test_testing.py index c25a0305..a673c2e1 100644 --- a/tests/test_testing.py +++ b/tests/test_testing.py @@ -207,7 +207,6 @@ def test_session_transaction_needs_cookies(app): def test_test_client_context_binding(app, client): - app.config['LOGGER_HANDLER_POLICY'] = 'never' app.testing = False @app.route('/') From ed1f6047271dc7ba1ee9c82e0c257b8ac406e80c Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 31 Jul 2017 13:58:50 -0700 Subject: [PATCH 327/399] Update CHANGES --- CHANGES | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index fe48808f..cb0579cb 100644 --- a/CHANGES +++ b/CHANGES @@ -105,7 +105,7 @@ Major release, unreleased from ``.env`` and ``.flaskenv`` files if python-dotenv is installed. (`#2416`_) - When passing a full URL to the test client, use the scheme in the URL instead - of the ``PREFERRED_URL_SCHEME``. (`#2436`_) + of the ``PREFERRED_URL_SCHEME``. (`#2430`_) - ``app.logger`` has been simplified. ``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` config was removed. The logger is always named ``flask.app``. The level is only set on first access, it doesn't check @@ -142,6 +142,7 @@ Major release, unreleased .. _#2412: https://github.com/pallets/flask/pull/2412 .. _#2414: https://github.com/pallets/flask/pull/2414 .. _#2416: https://github.com/pallets/flask/pull/2416 +.. _#2430: https://github.com/pallets/flask/pull/2430 .. _#2436: https://github.com/pallets/flask/pull/2436 Version 0.12.2 From 2efb565fbc293c9395e735316024d56e03fb0350 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 1 Aug 2017 08:28:32 -0700 Subject: [PATCH 328/399] mention that session signature checks max age add expiration to cookie security docs closes #2422 --- docs/config.rst | 9 ++++++--- docs/security.rst | 31 ++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index cf119cba..6e30466e 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -167,9 +167,12 @@ The following configuration values are used internally by Flask: .. py:data:: PERMANENT_SESSION_LIFETIME - If ``session.permanent`` is true, the cookie's max age will be set to this - number of seconds. Can either be a :class:`datetime.timedelta` or an - ``int``. + If ``session.permanent`` is true, the cookie's expiration will be set this + number of seconds in the future. Can either be a + :class:`datetime.timedelta` or an ``int``. + + Flask's default cookie implementation validates that the cryptographic + signature is not older than this value. Default: ``timedelta(days=31)`` (``2678400`` seconds) diff --git a/docs/security.rst b/docs/security.rst index 97c93292..d1ce6515 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -206,7 +206,36 @@ They can be set on other cookies too. response.set_cookie('username', 'flask', secure=True, httponly=True) -- https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#Secure_and_HttpOnly_cookies +Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after +the given time, or the current time plus the age, respectively. If neither +option is set, the cookie will be removed when the browser is closed. :: + + # cookie expires after 10 minutes + response.set_cookie('snakes', '3', max_age=600) + +For the session cookie, if ``session.permanent`` is set, then +:data:`SESSION_COOKIE_LIFETIME` is used to set the expiration. Flask's default +cookie implementation validates that the cryptographic signature is not older +than this value. Lowering this value may help mitigate replay attacks, where +intercepted cookies can be sent at a later time. + + app.config.update( + PERMANENT_SESSION_LIFETIME=600 + ) + + @app.route('/login', methods=['POST']) + def login(): + ... + session.clear() + session['user_id'] = user.id + session.permanent = True + ... + +Use :class:`TimedSerializer` to sign and validate other cookie values (or any +values that need secure signatures). + +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies +- https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie HTTP Public Key Pinning (HPKP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ From 1c35b98f0bcb1a1362cdfb1663992e6526e63a67 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 1 Aug 2017 08:39:42 -0700 Subject: [PATCH 329/399] fix code block --- docs/security.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/security.rst b/docs/security.rst index d1ce6515..fe1351ad 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -217,7 +217,7 @@ For the session cookie, if ``session.permanent`` is set, then :data:`SESSION_COOKIE_LIFETIME` is used to set the expiration. Flask's default cookie implementation validates that the cryptographic signature is not older than this value. Lowering this value may help mitigate replay attacks, where -intercepted cookies can be sent at a later time. +intercepted cookies can be sent at a later time. :: app.config.update( PERMANENT_SESSION_LIFETIME=600 From 97ad0859128a1e6f2ec3efb9efc46c7c40646318 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 1 Aug 2017 08:47:56 -0700 Subject: [PATCH 330/399] fix links --- docs/conf.py | 3 ++- docs/security.rst | 14 +++++++------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5f6bd624..94cae16d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -267,9 +267,10 @@ intersphinx_mapping = { 'werkzeug': ('http://werkzeug.pocoo.org/docs/', None), 'click': ('http://click.pocoo.org/', None), 'jinja': ('http://jinja.pocoo.org/docs/', None), + 'itsdangerous': ('https://pythonhosted.org/itsdangerous', None), 'sqlalchemy': ('https://docs.sqlalchemy.org/en/latest/', None), 'wtforms': ('https://wtforms.readthedocs.io/en/latest/', None), - 'blinker': ('https://pythonhosted.org/blinker/', None) + 'blinker': ('https://pythonhosted.org/blinker/', None), } html_theme_options = { diff --git a/docs/security.rst b/docs/security.rst index fe1351ad..13ea2e33 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -213,11 +213,11 @@ option is set, the cookie will be removed when the browser is closed. :: # cookie expires after 10 minutes response.set_cookie('snakes', '3', max_age=600) -For the session cookie, if ``session.permanent`` is set, then -:data:`SESSION_COOKIE_LIFETIME` is used to set the expiration. Flask's default -cookie implementation validates that the cryptographic signature is not older -than this value. Lowering this value may help mitigate replay attacks, where -intercepted cookies can be sent at a later time. :: +For the session cookie, if :attr:`session.permanent ` +is set, then :data:`PERMANENT_SESSION_LIFETIME` is used to set the expiration. +Flask's default cookie implementation validates that the cryptographic +signature is not older than this value. Lowering this value may help mitigate +replay attacks, where intercepted cookies can be sent at a later time. :: app.config.update( PERMANENT_SESSION_LIFETIME=600 @@ -231,8 +231,8 @@ intercepted cookies can be sent at a later time. :: session.permanent = True ... -Use :class:`TimedSerializer` to sign and validate other cookie values (or any -values that need secure signatures). +Use :class:`itsdangerous.TimedSerializer` to sign and validate other cookie +values (or any values that need secure signatures). - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie From 392989aaa7f6f5e90a623845cb859451b207ca93 Mon Sep 17 00:00:00 2001 From: hallazzang Date: Sun, 6 Aug 2017 09:06:19 +0900 Subject: [PATCH 331/399] corrected indentations for pypa description --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index ff2f3107..bb2aab41 100644 --- a/setup.py +++ b/setup.py @@ -31,9 +31,9 @@ And run it: $ pip install Flask $ python hello.py - * Running on http://localhost:5000/ + * Running on http://localhost:5000/ - Ready for production? `Read this first `. +Ready for production? `Read this first `. Links ````` From 7640a1f1c9ff47798d9bc31d0e80af0a024e8936 Mon Sep 17 00:00:00 2001 From: Brennan Vincello Date: Thu, 10 Aug 2017 21:51:02 -0700 Subject: [PATCH 332/399] Add version warning --- docs/tutorial/introduction.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/tutorial/introduction.rst b/docs/tutorial/introduction.rst index 67008435..ed984715 100644 --- a/docs/tutorial/introduction.rst +++ b/docs/tutorial/introduction.rst @@ -22,6 +22,11 @@ connections in a more intelligent way, allowing you to target different relational databases at once and more. You might also want to consider one of the popular NoSQL databases if your data is more suited for those. +.. warning:: + If you're following the tutorial from a specific version of the docs, be + sure to check out the same tag in the repository, otherwise the tutorial + may be different than the example. + Here is a screenshot of the final application: .. image:: ../_static/flaskr.png From 5888d76342ef6005af20c0b9a42426d17118570e Mon Sep 17 00:00:00 2001 From: Chuan Ma Date: Mon, 14 Aug 2017 10:25:24 -0400 Subject: [PATCH 333/399] pytest.yield_fixture deprecated in pytest 3.0+. It's suggested to use pytest.fixture directly, and flask already requires 'pytest>=3'. --- tests/conftest.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/conftest.py b/tests/conftest.py index 3cca3fbf..3a8ae69e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -161,7 +161,7 @@ def purge_module(request): return inner -@pytest.yield_fixture(autouse=True) +@pytest.fixture(autouse=True) def catch_deprecation_warnings(recwarn): yield gc.collect() From 7c510199850957161240f081359e2246d8982be1 Mon Sep 17 00:00:00 2001 From: Arie Marie 'Thomas' Dalleis Date: Tue, 15 Aug 2017 09:55:38 -0400 Subject: [PATCH 334/399] Fix typos and formatting --- examples/flaskr/README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/flaskr/README b/examples/flaskr/README index f60287fa..5d09faa7 100644 --- a/examples/flaskr/README +++ b/examples/flaskr/README @@ -10,7 +10,7 @@ ~ How do I use it? 1. edit the configuration in the factory.py file or - export an FLASKR_SETTINGS environment variable + export a FLASKR_SETTINGS environment variable pointing to a configuration file or pass in a dictionary with config values using the create_app function. @@ -19,7 +19,7 @@ pip install --editable . - 3. Instruct flask to use the right application + 3. instruct flask to use the right application export FLASK_APP=flaskr.factory:create_app() From 154c62123737a6ff047a2b4b8e3b58f75f9dfc95 Mon Sep 17 00:00:00 2001 From: Alejandro Mantecon Guillen Date: Fri, 25 Aug 2017 09:36:26 -0400 Subject: [PATCH 335/399] Update debugger and flaskr screenshots (#2454) --- docs/_static/debugger.png | Bin 123545 -> 213851 bytes docs/_static/flaskr.png | Bin 53571 -> 72263 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/_static/debugger.png b/docs/_static/debugger.png index 4f47229d6a24d5e42980c274591b1de30d7ff0d5..558a9114596d49b0639e7871b7e71f14c18fc89e 100644 GIT binary patch literal 213851 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�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

    di;#eU>C6_-&zIx` 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 literal 3624 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 a8@&K-C`7nB;GvKJ0000R^-r delta 260 zcmV+f0sH>(0hI!f8Gi-<001BJ|6u?C02y>eSaefwW^{L9a%BKPWN%_+AW3auXJt}l zVPtu6$z?nM006g1L_t(|+G1oNAJ_n~E4i98f%p#)PasEgDG>jM;zm+6CnISF;R;eT zTLSS!hz0G?EQ=Qa1Q-DMm`gPzOzE=#0000< KMNUMnLSTX^?PY!d From 851ca95778ded1b4b97fa92ddd45ab5b43fdcd90 Mon Sep 17 00:00:00 2001 From: Grey Li Date: Sat, 23 Sep 2017 22:26:48 +0800 Subject: [PATCH 339/399] Fix typo in comment --- flask/helpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/helpers.py b/flask/helpers.py index 5adb0e2a..efec454c 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -383,7 +383,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 session object, which is not true for session + # 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 3a8cc3766363a6d4376a08263b70e6a143b32221 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 28 Sep 2017 10:58:53 -0700 Subject: [PATCH 340/399] install argparse for 2.6 closes #2476 --- tox.ini | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/tox.ini b/tox.ini index ff4d7999..45d81b65 100644 --- a/tox.ini +++ b/tox.ini @@ -59,13 +59,8 @@ passenv = CI TRAVIS TRAVIS_* deps = codecov skip_install = true commands = + # install argparse for 2.6 + python -c 'import sys, pip; sys.version_info < (2, 7) and pip.main(["install", "argparse", "-q"])' coverage combine coverage report codecov - -[testenv:detox] -skip_install = true -deps = detox -commands = - detox -e py{36,35,34,33,27,26,py},py{36,27,py}-simplejson,py{36,33,27,26,py}-devel,py{36,33,27,26,py}-lowest - tox -e docs-html,coverage-report From 0a1090890f8425a69e5b004aceb04fd7527b8e35 Mon Sep 17 00:00:00 2001 From: Henry Chladil Date: Sun, 8 Oct 2017 03:52:44 +1000 Subject: [PATCH 341/399] Add additional information in the url_rule docs (#2467) If a user is making use of the errorhandler(405) decorator, and they wish to include an Allow header (as per HTTP spec), they may be confused to find that url_rule is None. This doc change aims to clarify that because the request was never successfully matched, it was never bound to a URL rule and to access the valid methods, they must examine routing_exception. --- flask/wrappers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/flask/wrappers.py b/flask/wrappers.py index 8c559c0e..807059d0 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -119,6 +119,10 @@ class Request(RequestBase, JSONMixin): #: 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. + #: Though if the request's method was invalid for the URL rule, + #: the valid list is available in ``routing_exception.valid_methods`` + #: instead (an attribute of the Werkzeug exception :exc:`~werkzeug.exceptions.MethodNotAllowed`) + #: because the request was never internally bound. #: #: .. versionadded:: 0.6 url_rule = None From 2f57a0b917478a028859b9d036b59cf1bba6cad2 Mon Sep 17 00:00:00 2001 From: Caratpine Date: Sat, 19 Aug 2017 11:41:03 +0800 Subject: [PATCH 342/399] Blueprint view function name should not contain dots --- CHANGES | 2 ++ flask/blueprints.py | 2 ++ tests/test_blueprints.py | 9 +++++++++ 3 files changed, 13 insertions(+) diff --git a/CHANGES b/CHANGES index cb0579cb..357b289d 100644 --- a/CHANGES +++ b/CHANGES @@ -112,6 +112,7 @@ Major release, unreleased ``app.debug`` each time. Only one format is used, not different ones depending on ``app.debug``. No handlers are removed, and a handler is only added if no handlers are already configured. (`#2436`_) +- Blueprint view function name may not contain dots. (`#2450`_) .. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1489: https://github.com/pallets/flask/pull/1489 @@ -144,6 +145,7 @@ Major release, unreleased .. _#2416: https://github.com/pallets/flask/pull/2416 .. _#2430: https://github.com/pallets/flask/pull/2430 .. _#2436: https://github.com/pallets/flask/pull/2436 +.. _#2450: https://github.com/pallets/flask/pull/2450 Version 0.12.2 -------------- diff --git a/flask/blueprints.py b/flask/blueprints.py index 80668dbe..4c9938e2 100644 --- a/flask/blueprints.py +++ b/flask/blueprints.py @@ -198,6 +198,8 @@ class Blueprint(_PackageBoundObject): """ if endpoint: assert '.' not in endpoint, "Blueprint endpoints should not contain dots" + if view_func: + assert '.' not in view_func.__name__, "Blueprint view function name should not contain dots" self.record(lambda s: s.add_url_rule(rule, endpoint, view_func, **options)) diff --git a/tests/test_blueprints.py b/tests/test_blueprints.py index d57b3034..c58a0d3b 100644 --- a/tests/test_blueprints.py +++ b/tests/test_blueprints.py @@ -360,6 +360,15 @@ def test_route_decorator_custom_endpoint_with_dots(app, client): lambda: None ) + foo_foo_foo.__name__ = 'bar.123' + + pytest.raises( + AssertionError, + lambda: bp.add_url_rule( + '/bar/123', view_func=foo_foo_foo + ) + ) + app.register_blueprint(bp, url_prefix='/py') assert client.get('/py/foo').data == b'bp.foo' From 5436dddf64f0103088c4ca212f16efa40c88766a Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 14 Jul 2017 20:34:23 -0700 Subject: [PATCH 343/399] rewrite cli errors consistent order for arguments to load functions refactor find_app_by_string to flow better more cli loader tests --- flask/cli.py | 127 +++++++++++++++++------------- tests/test_apps/cliapp/factory.py | 4 + tests/test_cli.py | 8 +- 3 files changed, 81 insertions(+), 58 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index bea4a29f..98056e27 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -46,6 +46,7 @@ def find_best_app(script_info, module): # Search for the most common names first. for attr_name in ('app', 'application'): app = getattr(module, attr_name, None) + if isinstance(app, Flask): return app @@ -58,9 +59,9 @@ def find_best_app(script_info, module): return matches[0] elif len(matches) > 1: raise NoAppException( - 'Auto-detected multiple Flask applications in module "{module}".' - ' Use "FLASK_APP={module}:name" to specify the correct' - ' one.'.format(module=module.__name__) + 'Detected multiple Flask applications in module "{module}". Use ' + '"FLASK_APP={module}:name" to specify the correct ' + 'one.'.format(module=module.__name__) ) # Search for app factory functions. @@ -69,25 +70,29 @@ def find_best_app(script_info, module): if inspect.isfunction(app_factory): try: - app = call_factory(app_factory, script_info) + app = call_factory(script_info, app_factory) + if isinstance(app, Flask): return app except TypeError: raise NoAppException( - 'Auto-detected "{function}()" in module "{module}", but ' - 'could not call it without specifying arguments.'.format( - function=attr_name, module=module.__name__ + 'Detected factory "{factory}" in module "{module}", but ' + 'could not call it without arguments. Use ' + '"FLASK_APP=\'{module}:{factory}(args)\'" to specify ' + 'arguments.'.format( + factory=attr_name, module=module.__name__ ) ) raise NoAppException( - 'Failed to find application in module "{module}". Are you sure ' - 'it contains a Flask application? Maybe you wrapped it in a WSGI ' - 'middleware.'.format(module=module.__name__) + 'Failed to find Flask application or factory in module "{module}". ' + 'Use "FLASK_APP={module}:name to specify one.'.format( + module=module.__name__ + ) ) -def call_factory(app_factory, script_info, arguments=()): +def call_factory(script_info, app_factory, arguments=()): """Takes an app factory, a ``script_info` object and optionally a tuple of arguments. Checks for the existence of a script_info argument and calls the app_factory depending on that and the arguments provided. @@ -102,54 +107,65 @@ def call_factory(app_factory, script_info, arguments=()): return app_factory(*arguments) elif not arguments and len(arg_names) == 1 and arg_defaults is None: return app_factory(script_info) + return app_factory() -def find_app_by_string(string, script_info, module): - """Checks if the given string is a variable name or a function. If it is - a function, it checks for specified arguments and whether it takes - a ``script_info`` argument and calls the function with the appropriate - arguments.""" - from . import Flask - function_regex = r'^(?P\w+)(?:\((?P.*)\))?$' - match = re.match(function_regex, string) - if match: - name, args = match.groups() +def find_app_by_string(script_info, module, app_name): + """Checks if the given string is a variable name or a function. If it is a + function, it checks for specified arguments and whether it takes a + ``script_info`` argument and calls the function with the appropriate + arguments. + """ + from flask import Flask + match = re.match(r'^ *([^ ()]+) *(?:\((.*?) *,? *\))? *$', app_name) + + if not match: + raise NoAppException( + '"{name}" is not a valid variable name or function ' + 'expression.'.format(name=app_name) + ) + + name, args = match.groups() + + try: + attr = getattr(module, name) + except AttributeError as e: + raise NoAppException(e.args[0]) + + if inspect.isfunction(attr): + if args: + try: + args = ast.literal_eval('({args},)'.format(args=args)) + except (ValueError, SyntaxError)as e: + raise NoAppException( + 'Could not parse the arguments in ' + '"{app_name}".'.format(e=e, app_name=app_name) + ) + else: + args = () + try: - if args is not None: - args = args.rstrip(' ,') - if args: - args = ast.literal_eval( - "({args}, )".format(args=args)) - else: - args = () - app_factory = getattr(module, name, None) - app = call_factory(app_factory, script_info, args) - else: - attr = getattr(module, name, None) - if inspect.isfunction(attr): - app = call_factory(attr, script_info) - else: - app = attr - - if isinstance(app, Flask): - return app - else: - raise NoAppException('Failed to find application in module ' - '"{name}"'.format(name=module)) + app = call_factory(script_info, attr, args) except TypeError as e: - new_error = NoAppException( - '{e}\nThe app factory "{factory}" in module "{module}" could' - ' not be called with the specified arguments (and a' - ' script_info argument automatically added if applicable).' - ' Did you make sure to use the right number of arguments as' - ' well as not using keyword arguments or' - ' non-literals?'.format(e=e, factory=string, module=module)) - reraise(NoAppException, new_error, sys.exc_info()[2]) + raise NoAppException( + '{e}\nThe factory "{app_name}" in module "{module}" could not ' + 'be called with the specified arguments.'.format( + e=e, app_name=app_name, module=module.__name__ + ) + ) else: - raise NoAppException( - 'The provided string "{string}" is not a valid variable name' - 'or function expression.'.format(string=string)) + app = attr + + if isinstance(app, Flask): + return app + + raise NoAppException( + 'A valid Flask application was not obtained from ' + '"{module}:{app_name}".'.format( + module=module.__name__, app_name=app_name + ) + ) def prepare_import(path): @@ -181,7 +197,6 @@ def prepare_import(path): def locate_app(script_info, module_name, app_name, raise_if_not_found=True): - """Attempts to locate the application.""" __traceback_hide__ = True try: @@ -206,7 +221,7 @@ def locate_app(script_info, module_name, app_name, raise_if_not_found=True): if app_name is None: return find_best_app(script_info, module) else: - return find_app_by_string(app_name, script_info, module) + return find_app_by_string(script_info, module, app_name) def get_version(ctx, param, value): @@ -312,7 +327,7 @@ class ScriptInfo(object): app = None if self.create_app is not None: - app = call_factory(self.create_app, self) + app = call_factory(self, self.create_app) else: if self.app_import_path: path, name = (self.app_import_path.split(':', 1) + [None])[:2] diff --git a/tests/test_apps/cliapp/factory.py b/tests/test_apps/cliapp/factory.py index 2e8598e8..95d60396 100644 --- a/tests/test_apps/cliapp/factory.py +++ b/tests/test_apps/cliapp/factory.py @@ -13,3 +13,7 @@ def create_app2(foo, bar): def create_app3(foo, script_info): return Flask('_'.join(['app3', foo, script_info.data['test']])) + + +def no_app(): + pass diff --git a/tests/test_cli.py b/tests/test_cli.py index c66bd17e..811ef0c8 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -200,6 +200,8 @@ def test_prepare_import(request, value, path, result): ('cliapp.factory', 'create_app2("foo", "bar", )', 'app2_foo_bar'), # takes script_info ('cliapp.factory', 'create_app3("foo")', 'app3_foo_spam'), + # strip whitespace + ('cliapp.factory', ' create_app () ', 'app'), )) def test_locate_app(test_apps, iname, aname, result): info = ScriptInfo() @@ -213,12 +215,14 @@ def test_locate_app(test_apps, iname, aname, result): ('cliapp.app', 'notanapp'), # not enough arguments ('cliapp.factory', 'create_app2("foo")'), + # invalid identifier + ('cliapp.factory', 'create_app('), + # no app returned + ('cliapp.factory', 'no_app'), # nested import error ('cliapp.importerrorapp', None), # not a Python file ('cliapp.message.txt', None), - # space before arg list - ('cliapp.factory', 'create_app ()'), )) def test_locate_app_raises(test_apps, iname, aname): info = ScriptInfo() From 9bc329c0c9dcdb5a3a6919f31542b6e7453d8aa8 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 10 Oct 2017 11:10:20 -0700 Subject: [PATCH 344/399] rewrite cli docs reflects the current FLASK_APP detection --- docs/cli.rst | 496 +++++++++++++++++++++++------------------- docs/installation.rst | 3 + flask/cli.py | 42 ++-- 3 files changed, 297 insertions(+), 244 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index b481991d..cdb0fc59 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -1,145 +1,148 @@ +.. currentmodule:: flask + .. _cli: Command Line Interface ====================== -.. versionadded:: 0.11 +Installing Flask installs the ``flask`` script, a `Click`_ command line +interface, in your virtualenv. Executed from the terminal, this script gives +access to built-in, extension, and application-defined commands. The ``--help`` +option will give more information about any commands and options. -.. currentmodule:: flask +.. _Click: http://click.pocoo.org/ -One of the nice new features in Flask 0.11 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 ------------ +Application Discovery +--------------------- -After installation of Flask you will now find a :command:`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 -m flask`` to accomplish -exactly the same. +The ``flask`` command is installed by Flask, not your application; it must be +told where to find your application in order to use it. The ``FLASK_APP`` +environment variable is used to specify how to load the application. -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 desire so. +Unix Bash (Linux, Mac, etc.):: -For the :command:`flask` script to work, an application needs to be discovered. -Flask looks for a module named :file:`wsgi.py` or :file:`app.py` by default, -and if it finds one it assumes the application is defined in it. + $ export FLASK_APP=hello + $ flask run -You can instruct Flask to look for the application in a different module by -exporting the ``FLASK_APP`` environment variable. It can be either set to an -import path or to a filename of a Python module that contains a Flask -application. +Windows CMD:: -In that imported file the name of the app needs to be called ``app`` or -optionally be specified after a colon. For instance -``mymodule:application`` would tell it to use the `application` object in -the :file:`mymodule.py` file. + > set FLASK_APP=hello + > flask run -Given a :file:`hello.py` file with the application in it named ``app`` -this is how it can be run. +Windows PowerShell:: -Environment variables (On Windows use ``set`` instead of ``export``):: + > $env:FLASK_APP = "hello" + > flask run - export FLASK_APP=hello - flask run +While ``FLASK_APP`` supports a variety of options for specifying your +application, most use cases should be simple. Here are the typical values: -Or with a filename:: +(nothing) + The file :file:`wsgi.py` is imported, automatically detecting an app + (``app``). This provides an easy way to create an app from a factory with + extra arguments. - export FLASK_APP=/path/to/hello.py - flask run +``FLASK_APP=hello`` + The name is imported, automatically detecting an app (``app``) or factory + (``create_app``). -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. +``FLASK_APP`` has three parts: an optional path that sets the current working +directory, a Python file or dotted import path, and an optional variable +name of the instance or factory. If the name is a factory, it can optionally +be followed by arguments in parentheses. The following values demonstrate these +parts: -Edit the activate script for the shell you use. For example: +``FLASK_APP=src/hello`` + Sets the current working directory to ``src`` then imports ``hello``. -Unix Bash: ``venv/bin/activate``:: +``FLASK_APP=hello.web`` + Imports the path ``hello.web``. - FLASK_APP=hello - export FLASK_APP +``FLASK_APP=hello:app2`` + Uses the ``app2`` Flask instance in ``hello``. -Windows CMD.exe: ``venv\Scripts\activate.bat``:: +``FLASK_APP="hello:create_app('dev')"`` + The ``create_app`` factory in ``hello`` is called with the string ``'dev'`` + as the argument. - set "FLASK_APP=hello" - :END +If ``FLASK_APP`` is not set, the command will look for a file called +:file:`wsgi.py` or :file:`app.py` and try to detect an application instance or +factory. -Debug Flag ----------- +Within the given import, the command looks for an application instance named +``app`` or ``application``, then any application instance. If no instance is +found, the command looks for a factory function named ``create_app`` or +``make_app`` that returns an instance. -The :command:`flask` script can also be instructed to enable the debug -mode of the application automatically by exporting ``FLASK_DEBUG``. If -set to ``1`` debug is enabled or ``0`` disables it:: +When calling an application factory, if the factory takes an argument named +``info``, then the :class:`~cli.ScriptInfo` instance is passed as a keyword +argument. If parentheses follow the factory name, their contents are parsed +as Python literals and passes as arguments to the function. This means that +strings must still be in quotes. - export FLASK_DEBUG=1 -Running a Shell ---------------- +Run the Development Server +-------------------------- -To run an interactive Python shell you can use the ``shell`` command:: +The :func:`run ` command will start the development server. It +replaces the :meth:`Flask.run` method in most cases. :: - flask shell + $ flask run + * Serving Flask app "hello" + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) -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`. +.. warning:: Do not use this command to run your application in production. + Only use the development server during development. The development server + is provided for convenience, but is not designed to be particularly secure, + stable, or efficient. See :ref:`deployment` for how to run in production. -Custom Commands ---------------- - -If you want to add more commands to the shell script you can do this -easily. For instance if you want a shell command to initialize the database you -can do this:: - - import click - from flask import Flask - app = Flask(__name__) +Open a Shell +------------ - @app.cli.command() - def initdb(): - """Initialize the database.""" - click.echo('Init the db') +To explore the data in your application, you can start an interactive Python +shell with the :func:`shell ` command. An application +context will be active, and the app instance will be imported. :: -The command will then show up on the command line:: + $ flask shell + Python 3.6.2 (default, Jul 20 2017, 03:52:27) + [GCC 7.1.1 20170630] on linux + App: example + Instance: /home/user/Projects/hello/instance + >>> - $ flask initdb - Init the db +Use :meth:`~Flask.shell_context_processor` to add other automatic imports. -Application Context -------------------- -Most commands operate on the application so it makes a lot of sense if -they have the application context setup. Because of this, if you register -a callback on ``app.cli`` with the :meth:`~flask.cli.AppGroup.command` the -callback will automatically be wrapped through :func:`cli.with_appcontext` -which informs the cli system to ensure that an application context is set -up. This behavior is not available if a command is added later with -:func:`~click.Group.add_command` or through other means. +Debug Mode +---------- -It can also be disabled by passing ``with_appcontext=False`` to the -decorator:: +Set the :envvar:`FLASK_DEBUG` environment variable to override the +application's :attr:`~Flask.debug` flag. The value ``1`` enables it, ``0`` +disables it. Forcing the debug flag on also enables the debugger and reloader +when running the development server. :: - @app.cli.command(with_appcontext=False) - def example(): - pass + $ FLASK_DEBUG=1 flask run + * Serving Flask app "hello" + * Forcing debug mode on + * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) + * Restarting with inotify reloader + * Debugger is active! + * Debugger PIN: 223-456-919 .. _dotenv: -Loading Environment Variables From ``.env`` Files -------------------------------------------------- +Environment Variables From dotenv +--------------------------------- + +Rather than setting ``FLASK_APP`` each time you open a new terminal, you can +use Flask's dotenv support to set environment variables automatically. -If `python-dotenv`_ is installed, running the :command:`flask` command will set +If `python-dotenv`_ is installed, running the ``flask`` command will set environment variables defined in the files :file:`.env` and :file:`.flaskenv`. This can be used to avoid having to set ``FLASK_APP`` manually every time you open a new terminal, and to set configuration using environment variables @@ -150,186 +153,231 @@ which are used over those set in :file:`.flaskenv`. :file:`.flaskenv` should be used for public variables, such as ``FLASK_APP``, while :file:`.env` should not be committed to your repository so that it can set private variables. -Directories are scanned upwards from the directory you call :command:`flask` +Directories are scanned upwards from the directory you call ``flask`` from to locate the files. The current working directory will be set to the location of the file, with the assumption that that is the top level project directory. -The files are only loaded by the :command:`flask` command or calling -:meth:`~flask.Flask.run`. If you would like to load these files when running in -production, you should call :func:`~flask.cli.load_dotenv` manually. +The files are only loaded by the ``flask`` command or calling +:meth:`~Flask.run`. If you would like to load these files when running in +production, you should call :func:`~cli.load_dotenv` manually. .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme -Factory Functions ------------------ +Environment Variables From virtualenv +------------------------------------- + +If you do not want to install dotenv support, you can still set environment +variables by adding them to the end of the virtualenv's :file:`activate` +script. Activating the virtualenv will set the variables. + +Unix Bash, :file:`venv/bin/activate`:: + + export FLASK_APP=hello + +Windows CMD, :file:`venv\Scripts\activate.bat`:: + + set FLASK_APP=hello + +It is preferred to use dotenv support over this, since :file:`.flaskenv` can be +committed to the repository so that it works automatically wherever the project +is checked out. + + +Custom Commands +--------------- + +The ``flask`` command is implemented using `Click`_. See that project's +documentation for full information about writing commands. + +This example adds the command ``create_user`` that takes the argument +``name``. :: + + import click + from flask import Flask -In case you are using factory functions to create your application (see -:ref:`app-factories`) you will discover that the :command:`flask` command -cannot work with them directly. Flask won't be able to figure out how to -instantiate your application properly by itself. Because of this reason -the recommendation is to create a separate file that instantiates -applications. This is not the only way to make this work. Another is the -:ref:`custom-scripts` support. + app = Flask(__name__) -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. + @app.cli.command() + @click.argument('name') + def create_user(name): + ... -This could be a file named :file:`autoapp.py` with these contents:: +:: - import os - from yourapplication import create_app - app = create_app(os.environ['YOURAPPLICATION_CONFIG']) + flask create_user admin -Once this has happened you can make the :command:`flask` command automatically -pick it up:: +This example adds the same command, but as ``user create``, a command in a +group. This is useful if you want to organize multiple related commands. :: - export YOURAPPLICATION_CONFIG=/path/to/config.cfg - export FLASK_APP=/path/to/autoapp.py + import click + from flask import Flask + from flask.cli import AppGroup + + app = Flask(__name__) + user_cli = AppGroup('user') + + @user_cli.command('create') + @click.argument('name') + def create_user(name): + ... + + app.cli.add_command(user_cli) + +:: + + flask user create demo + + +Application Context +~~~~~~~~~~~~~~~~~~~ + +Commands added using the Flask app's :attr:`~Flask.cli` +:meth:`~cli.AppGroup.command` decorator will be executed with an application +context pushed, so your command and extensions have access to the app and its +configuration. If you create a command using the Click :func:`~click.command` +decorator instead of the Flask decorator, you can use +:func:`~cli.with_appcontext` to get the same behavior. :: + + import click + from flask.cli import with_appcontext + + @click.command + @with_appcontext + def do_work(): + ... + + app.cli.add_command(do_work) + +If you're sure a command doesn't need the context, you can disable it:: + + @app.cli.command(with_appcontext=False) + def do_work(): + ... + + +Plugins +------- + +Flask will automatically load commands specified in the ``flask.commands`` +`entry point`_. This is useful for extensions that want to add commands when +they are installed. Entry points are specified in :file:`setup.py` :: + + from setuptools import setup + + setup( + name='flask-my-extension', + ..., + entry_points={ + 'flask.commands': [ + 'my-command=flask_my_extension.commands:cli' + ], + }, + ) + + +.. _entry point: https://packaging.python.org/tutorials/distributing-packages/#entry-points + +Inside :file:`flask_my_extension/commands.py` you can then export a Click +object:: + + import click + + @click.command() + def cli(): + ... + +Once that package is installed in the same virtualenv as your Flask project, +you can run ``flask my-command`` to invoke the command. -From this point onwards :command:`flask` will find your application. .. _custom-scripts: Custom Scripts -------------- -While the most common way is to use the :command:`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 -:command:`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. - -To explain all of this, here is an example :file:`manage.py` script that -manages a hypothetical wiki application. We will go through the details -afterwards:: - - import os +When you are using the app factory pattern, it may be more convenient to define +your own Click script. Instead of using ``FLASK_APP`` and letting Flask load +your application, you can create your own Click object and export it as a +`console script`_ entry point. + +Create an instance of :class:`~cli.FlaskGroup` and pass it the factory:: + import click + from flask import Flask from flask.cli import FlaskGroup - def create_wiki_app(info): - from yourwiki import create_app - return create_app( - config=os.environ.get('WIKI_CONFIG', 'wikiconfig.py')) + def create_app(): + app = Flask('wiki') + # other setup + return app - @click.group(cls=FlaskGroup, create_app=create_wiki_app) + @click.group(cls=FlaskGroup, create_app=create_app) def cli(): - """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. 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. -2. The next thing we do is defining a function that is invoked with the - script info object (:class:`~flask.cli.ScriptInfo`) 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`). In this case we load the config from an - environment variable. -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 a 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. All is rounded up by invoking the script. - -CLI Plugins ------------ - -Flask extensions can always patch the :attr:`Flask.cli` instance with more -commands if they want. However there is a second way to add CLI plugins -to Flask which is through ``setuptools``. If you make a Python package that -should export a Flask command line plugin you can ship a :file:`setup.py` file -that declares an entrypoint that points to a click command: - -Example :file:`setup.py`:: + """Management script for the Wiki application.""" + +Define the entry point in :file:`setup.py`:: from setuptools import setup setup( name='flask-my-extension', - ... - entry_points=''' - [flask.commands] - my-command=mypackage.commands:cli - ''', + ..., + entry_points={ + 'console_scripts': [ + 'wiki=wiki:cli' + ], + }, ) -Inside :file:`mypackage/commands.py` you can then export a Click object:: +Install the application in the virtualenv in editable mode and the custom +script is available. Note that you don't need to set ``FLASK_APP``. :: - import click + $ pip install -e . + $ wiki run - @click.command() - def cli(): - """This is an example command.""" +.. _console script: https://packaging.python.org/tutorials/distributing-packages/#console-scripts -Once that package is installed in the same virtualenv as Flask itself you -can run ``flask my-command`` to invoke your command. This is useful to -provide extra functionality that Flask itself cannot ship. PyCharm Integration ------------------- -The new Flask CLI features aren’t yet fully integrated into the PyCharm IDE, -so we have to do a few tweaks to get them working smoothly. +The new Flask CLI features aren't yet fully integrated into the PyCharm IDE, +so we have to do a few tweaks to get them working smoothly. These instructions +should be similar for any other IDE you might want to use. -In your PyCharm application, with your project open, click on *Run* -from the menu bar and go to *Edit Configurations*. You’ll be greeted by a -screen similar to this: +In PyCharm, with your project open, click on *Run* from the menu bar and go to +*Edit Configurations*. You'll be greeted by a screen similar to this: .. image:: _static/pycharm-runconfig.png :align: center :class: screenshot :alt: screenshot of pycharm's run configuration settings -There’s quite a few options to change, but don’t worry— once we’ve done it -for one command, we can easily copy the entire configuration and make a -single tweak to give us access to other flask cli commands, including -any custom ones you may implement yourself. +There's quite a few options to change, but once we've done it for one command, +we can easily copy the entire configuration and make a single tweak to give us +access to other commands, including any custom ones you may implement yourself. -For the *Script* input (**A**), we want to navigate to the virtual environment -we’re using for our project and within that folder we want to pick the ``flask`` -file which will reside in the ``bin`` folder, or in the ``Scripts`` folder if -you're on Windows. +For the *Script* input (**A**), navigate to your project's virtual environment. +Within that folder, pick the ``flask`` executable which will reside in the +``bin`` folder, or in the ``Scripts`` on Windows. -The *Script Parameter* field (**B**) is set to the cli command you wish to -execute, in this example we use ``run`` which will run our development server. +The *Script Parameter* field (**B**) is set to the CLI command you to execute. +In this example we use ``run``, which will run the development server. -We need to add an environment variable (**C**) to identify our application. -Click on the browse button and add an entry with ``FLASK_APP`` on the -left and the name of the python file, or package on the right -(``app.py`` for example). +You can skip this next step if you're using :ref:`dotenv`. We need to add an +environment variable (**C**) to identify our application. Click on the browse +button and add an entry with ``FLASK_APP`` on the left and the name of the +Python file or package on the right (``app.py`` for example). Next we need to set the working directory (**D**) to be the same folder where -our application file or package resides. +our application file or package resides. PyCharm changed it to the directory +with the ``flask`` executable when we selected it earlier, which is incorrect. Finally, untick the *PYTHONPATH* options (**E**) and give the configuration a -good descriptive name, such as “Run Flask Server” and click *Apply*. +good descriptive name, such as "Run Flask Server", and click *Apply*. -Now that we have on run configuration which implements ``flask run`` from within -PyCharm, we can simply copy that configuration and alter the script argument -to run a different cli command, e.g. ``flask shell``. +Now that we have a configuration which runs ``flask run`` from within PyCharm, +we can simply copy that configuration and alter the *Script* argument +to run a different CLI command, e.g. ``flask shell``. diff --git a/docs/installation.rst b/docs/installation.rst index 0ae05f06..370ce48c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -43,10 +43,13 @@ use them if you install them. installed. * `python-dotenv`_ enables support for :ref:`dotenv` when running ``flask`` commands. +* `Watchdog`_ provides a faster, more efficient reloader for the development + server. .. _Blinker: https://pythonhosted.org/blinker/ .. _SimpleJSON: https://simplejson.readthedocs.io/ .. _python-dotenv: https://github.com/theskumar/python-dotenv#readme +.. _watchdog: https://pythonhosted.org/watchdog/ Virtual environments -------------------- diff --git a/flask/cli.py b/flask/cli.py index 98056e27..d4647401 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -234,11 +234,16 @@ def get_version(ctx, param, value): }, color=ctx.color) ctx.exit() -version_option = click.Option(['--version'], - help='Show the flask version', - expose_value=False, - callback=get_version, - is_flag=True, is_eager=True) + +version_option = click.Option( + ['--version'], + help='Show the flask version', + expose_value=False, + callback=get_version, + is_flag=True, + is_eager=True +) + class DispatchingApp(object): """Special application that dispatches to a Flask application which @@ -713,24 +718,21 @@ def routes_command(sort, all_methods): cli = FlaskGroup(help="""\ -This shell command acts as general utility script for Flask applications. - -It loads the application configured (through the FLASK_APP environment -variable) and then provides commands either provided by the application or -Flask itself. - -The most useful commands are the "run" and "shell" command. +A general utility script for Flask applications. -Example usage: +Provides commands from Flask, extensions, and the application. Loads the +application defined in the FLASK_APP environment variable, or from a wsgi.py +file. Debug mode can be controlled with the FLASK_DEBUG +environment variable. \b - %(prefix)s%(cmd)s FLASK_APP=hello.py - %(prefix)s%(cmd)s FLASK_DEBUG=1 - %(prefix)sflask run -""" % { - 'cmd': os.name == 'posix' and 'export' or 'set', - 'prefix': os.name == 'posix' and '$ ' or '', -}) + {prefix}{cmd} FLASK_APP=hello.py + {prefix}{cmd} FLASK_DEBUG=1 + {prefix}flask run +""".format( + cmd='export' if os.name == 'posix' else 'set', + prefix='$ ' if os.name == 'posix' else '> ' +)) def main(as_module=False): From c3527144780772a22effd718654ca6ad472aaae0 Mon Sep 17 00:00:00 2001 From: Mrn Om <31620258+mrnom@users.noreply.github.com> Date: Wed, 11 Oct 2017 16:47:26 +0300 Subject: [PATCH 345/399] Fix grammar/typos --- docs/patterns/fileuploads.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index 1c4b0d36..d0215c58 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -149,8 +149,8 @@ config key:: app = Flask(__name__) app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 -The code above will limited the maximum allowed payload to 16 megabytes. -If a larger file is transmitted, Flask will raise an +The code above will limit the maximum allowed payload to 16 megabytes. +If a larger file is transmitted, Flask will raise a :exc:`~werkzeug.exceptions.RequestEntityTooLarge` exception. This feature was added in Flask 0.6 but can be achieved in older versions From 9291ead32e2fc8b13cef825186c968944e9ff344 Mon Sep 17 00:00:00 2001 From: Radoslav Gerganov Date: Fri, 13 Oct 2017 14:53:50 +0300 Subject: [PATCH 346/399] Fix typo in logging.rst (#2492) Remove extra parenthesis from RequestFormatter constructor. --- docs/logging.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/logging.rst b/docs/logging.rst index 6bd9266c..36ed7c85 100644 --- a/docs/logging.rst +++ b/docs/logging.rst @@ -129,7 +129,7 @@ handler. :: formatter = RequestFormatter( '[%(asctime)s] %(remote_addr)s requested %(url)s\n' '%(levelname)s in %(module)s: %(message)s' - )) + ) default_handler.setFormatter(formatter) mail_handler.setFormatter(formatter) From b89cf95816ab700448c7bb0d43d3c2103af0ec15 Mon Sep 17 00:00:00 2001 From: Bogdan Alexandru Marginean Date: Thu, 19 Oct 2017 22:32:47 +0300 Subject: [PATCH 347/399] Update appfactories.rst --- docs/patterns/appfactories.rst | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index fdbde504..b6d4a729 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -89,10 +89,9 @@ For more information about the design of extensions refer to :doc:`/extensiondev Using Applications ------------------ -So to use such an application you then have to create the application -first in a separate file otherwise the :command:`flask` command won't be able -to find it. Here an example :file:`exampleapp.py` file that creates such -an application:: +To use such an application you have to create it in a separate file first, +otherwise the :command:`flask` command won't be able to find it. Here's an +example :file:`exampleapp.py` file that creates such an application:: from yourapplication import create_app app = create_app('/path/to/config.cfg') @@ -105,12 +104,12 @@ It can then be used with the :command:`flask` command:: Factory Improvements -------------------- -The factory function from above is not very clever so far, you can improve -it. The following changes are straightforward and possible: +The factory function above is not very clever, but you can improve it. +The following changes are straightforward to implement: -1. make it possible to pass in configuration values for unittests so that - you don't have to create config files on the filesystem -2. call a function from a blueprint when the application is setting up so +1. Make it possible to pass in configuration values for unit tests so that + you don't have to create config files on the filesystem. +2. Call a function from a blueprint when the application is setting up so that you have a place to modify attributes of the application (like - hooking in before / after request handlers etc.) -3. Add in WSGI middlewares when the application is creating if necessary. + hooking in before/after request handlers etc.) +3. Add in WSGI middlewares when the application is being created if necessary. From e3c853e60401bccf1564bee19dda7a3b41602ff9 Mon Sep 17 00:00:00 2001 From: Roy Crihfield <30845198+roysc2@users.noreply.github.com> Date: Tue, 24 Oct 2017 01:10:12 -0500 Subject: [PATCH 348/399] Fix typo in cli.py (#2502) --- flask/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index d4647401..8bfb21f6 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -644,7 +644,7 @@ def shell_command(): 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. + without having to manually configure the application. """ import code from flask.globals import _app_ctx_stack From f8321de36990faf199e050f4509077752264afca Mon Sep 17 00:00:00 2001 From: hugovk Date: Wed, 25 Oct 2017 09:03:27 +0300 Subject: [PATCH 349/399] Remove IRC notifications --- .travis.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index ed690253..972ddb0c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,10 +40,3 @@ branches: notifications: email: false - irc: - channels: - - "chat.freenode.net#pocoo" - on_success: change - on_failure: always - use_notice: true - skip_join: true From 50964fb4ab62fa857e9937656424d7e6ac9e738d Mon Sep 17 00:00:00 2001 From: domandinho Date: Thu, 9 Nov 2017 22:52:24 +0100 Subject: [PATCH 350/399] Minor fix in flaskr example README --- examples/flaskr/README | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/flaskr/README b/examples/flaskr/README index 5d09faa7..ab668d67 100644 --- a/examples/flaskr/README +++ b/examples/flaskr/README @@ -21,7 +21,7 @@ 3. instruct flask to use the right application - export FLASK_APP=flaskr.factory:create_app() + export FLASK_APP="flaskr.factory:create_app()" 4. initialize the database with this command: From 110a7525658902a586da751bcdf9780690c089b2 Mon Sep 17 00:00:00 2001 From: Per Mortensen Date: Wed, 22 Nov 2017 22:40:23 +0100 Subject: [PATCH 351/399] Update Twisted Web example --- 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 bf680976..5b0740a6 100644 --- a/docs/deploying/wsgi-standalone.rst +++ b/docs/deploying/wsgi-standalone.rst @@ -78,7 +78,7 @@ as well; see ``twistd -h`` and ``twistd web -h`` for more information. For example, to run a Twisted Web server in the foreground, on port 8080, with an application from ``myproject``:: - twistd -n web --port 8080 --wsgi myproject.app + twistd -n web --port tcp:8080 --wsgi myproject.app .. _Twisted: https://twistedmatrix.com/ .. _Twisted Web: https://twistedmatrix.com/trac/wiki/TwistedWeb From c23a63a185104b4dc39fe53d82254059c5ff4727 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 25 Nov 2017 00:05:57 +0100 Subject: [PATCH 352/399] Improved bad factory error handling --- flask/cli.py | 10 ++++++++++ tests/test_cli.py | 7 +++++++ 2 files changed, 17 insertions(+) diff --git a/flask/cli.py b/flask/cli.py index 8bfb21f6..b9d56b05 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -33,6 +33,12 @@ except ImportError: dotenv = None +def _called_with_wrong_args(factory, exc_info): + exc_type, exc_value, tb = exc_info + return exc_type is TypeError and \ + str(exc_value).startswith('%s() takes' % factory.__name__) + + class NoAppException(click.UsageError): """Raised if an application cannot be found or loaded.""" @@ -75,6 +81,8 @@ def find_best_app(script_info, module): if isinstance(app, Flask): return app except TypeError: + if not _called_with_wrong_args(app_factory, sys.exc_info()): + raise raise NoAppException( 'Detected factory "{factory}" in module "{module}", but ' 'could not call it without arguments. Use ' @@ -148,6 +156,8 @@ def find_app_by_string(script_info, module, app_name): try: app = call_factory(script_info, attr, args) except TypeError as e: + if not _called_with_wrong_args(attr, sys.exc_info()): + raise raise NoAppException( '{e}\nThe factory "{app_name}" in module "{module}" could not ' 'be called with the specified arguments.'.format( diff --git a/tests/test_cli.py b/tests/test_cli.py index 811ef0c8..d530b093 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -144,6 +144,13 @@ def test_find_best_app(test_apps): pytest.raises(NoAppException, find_best_app, script_info, Module) + class Module: + @staticmethod + def create_app(): + raise TypeError('bad bad factory!') + + pytest.raises(TypeError, find_best_app, script_info, Module) + @pytest.mark.parametrize('value,path,result', ( ('test', cwd, 'test'), From 2ef2000a39541d8424f3163ef5feeabacc81e681 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 25 Nov 2017 00:17:09 +0100 Subject: [PATCH 353/399] Enable threads by default for the dev server (#2529) Enable threads by default for dev server --- CHANGES | 1 + flask/cli.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 357b289d..294829ff 100644 --- a/CHANGES +++ b/CHANGES @@ -113,6 +113,7 @@ Major release, unreleased depending on ``app.debug``. No handlers are removed, and a handler is only added if no handlers are already configured. (`#2436`_) - Blueprint view function name may not contain dots. (`#2450`_) +- The dev server now uses threads by default. .. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1489: https://github.com/pallets/flask/pull/1489 diff --git a/flask/cli.py b/flask/cli.py index 8bfb21f6..1be7957c 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -592,7 +592,7 @@ def load_dotenv(path=None): @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, +@click.option('--with-threads/--without-threads', default=True, help='Enable or disable multithreading.') @pass_script_info def run_command(info, host, port, reload, debugger, eager_loading, From 1a2dc6f205003cd2307b33927b8628a3d97b7ce6 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 25 Nov 2017 00:18:10 +0100 Subject: [PATCH 354/399] Fixed tests for python 3 --- flask/cli.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index b9d56b05..0a556ddc 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -36,7 +36,9 @@ except ImportError: def _called_with_wrong_args(factory, exc_info): exc_type, exc_value, tb = exc_info return exc_type is TypeError and \ - str(exc_value).startswith('%s() takes' % factory.__name__) + str(exc_value).startswith(( + '%s() takes' % factory.__name__, + '%s() missing' % factory.__name__)) class NoAppException(click.UsageError): From c52e1b7388c17466a551391cdf81964bf0b7aef0 Mon Sep 17 00:00:00 2001 From: ThiefMaster Date: Thu, 23 Nov 2017 10:32:13 +0100 Subject: [PATCH 355/399] Fix ValueError for some invalid Range requests fixes #2526 --- CHANGES | 8 ++++++++ flask/helpers.py | 3 ++- tests/test_helpers.py | 21 ++++++++++++++++++++- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 3456276a..b32b98cb 100644 --- a/CHANGES +++ b/CHANGES @@ -15,6 +15,14 @@ Major release, unreleased method returns compressed response by default, and pretty response in debug mode. +Version 0.12.3 +-------------- + +Bugfix release, unreleased + +- Fix a ValueError caused by invalid Range requests in some cases + + Version 0.12.2 -------------- diff --git a/flask/helpers.py b/flask/helpers.py index 4bb1d1c9..caaad9a3 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -591,7 +591,8 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, rv = rv.make_conditional(request, accept_ranges=True, complete_length=fsize) except RequestedRangeNotSatisfiable: - file.close() + if file is not None: + file.close() raise else: rv = rv.make_conditional(request) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 9320ef71..69350751 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -468,7 +468,7 @@ class TestSendfile(object): @pytest.mark.skipif( not callable(getattr(Range, 'to_content_range_header', None)), - reason="not implement within werkzeug" + reason="not implemented within werkzeug" ) def test_send_file_range_request(self): app = flask.Flask(__name__) @@ -529,6 +529,25 @@ class TestSendfile(object): assert rv.status_code == 200 rv.close() + @pytest.mark.skipif( + not callable(getattr(Range, 'to_content_range_header', None)), + reason="not implemented within werkzeug" + ) + def test_send_file_range_request_xsendfile_invalid(self): + # https://github.com/pallets/flask/issues/2526 + app = flask.Flask(__name__) + app.use_x_sendfile = True + + @app.route('/') + def index(): + return flask.send_file('static/index.html', conditional=True) + + c = app.test_client() + + rv = c.get('/', headers={'Range': 'bytes=1000-'}) + assert rv.status_code == 416 + rv.close() + def test_attachment(self): app = flask.Flask(__name__) with app.test_request_context(): From 777cc17de34622a7f8324a3ce42fbc589d86476b Mon Sep 17 00:00:00 2001 From: Adrian Moennich Date: Sat, 25 Nov 2017 00:53:43 +0100 Subject: [PATCH 356/399] Fix broken test --- tests/test_helpers.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_helpers.py b/tests/test_helpers.py index 066f3687..d497c40d 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -571,6 +571,10 @@ class TestSendfile(object): # https://github.com/pallets/flask/issues/2526 app.use_x_sendfile = True + @app.route('/') + def index(): + return flask.send_file('static/index.html', conditional=True) + rv = client.get('/', headers={'Range': 'bytes=1000-'}) assert rv.status_code == 416 rv.close() From d6df70de809a51a4e1cfbb58f486e8d97a2eb439 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 26 Nov 2017 12:50:36 -0800 Subject: [PATCH 357/399] include tox.ini in sdist #2531 --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index f8d9c2ae..3616212e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,4 +1,4 @@ -include Makefile CHANGES LICENSE AUTHORS +include Makefile CHANGES LICENSE AUTHORS tox.ini graft artwork graft tests From 602c771327961ec28051b8fb60ba70e2091df565 Mon Sep 17 00:00:00 2001 From: Grey Li Date: Fri, 1 Dec 2017 16:03:52 +0800 Subject: [PATCH 358/399] Docs: update built-in server description --- docs/deploying/index.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst index 6f014bff..edf5a256 100644 --- a/docs/deploying/index.rst +++ b/docs/deploying/index.rst @@ -4,9 +4,8 @@ Deployment Options ================== While lightweight and easy to use, **Flask's built-in server is not suitable -for production** as it doesn't scale well and by default serves only one -request at a time. Some of the options available for properly running Flask in -production are documented here. +for production** as it doesn't scale well. Some of the options available for +properly running Flask in production are documented here. If you want to deploy your Flask application to a WSGI server not listed here, look up the server documentation about how to use a WSGI app with it. Just From 9520fbdcb21a43e3c30225c6643e7dd2d4c4a692 Mon Sep 17 00:00:00 2001 From: Hsiaoming Yang Date: Sat, 2 Dec 2017 17:09:28 +0900 Subject: [PATCH 359/399] Rename GitHub templates to md. They are Markdown. --- .github/{ISSUE_TEMPLATE.rst => ISSUE_TEMPLATE.md} | 0 .github/{PULL_REQUEST_TEMPLATE.rst => PULL_REQUEST_TEMPLATE.md} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename .github/{ISSUE_TEMPLATE.rst => ISSUE_TEMPLATE.md} (100%) rename .github/{PULL_REQUEST_TEMPLATE.rst => PULL_REQUEST_TEMPLATE.md} (100%) diff --git a/.github/ISSUE_TEMPLATE.rst b/.github/ISSUE_TEMPLATE.md similarity index 100% rename from .github/ISSUE_TEMPLATE.rst rename to .github/ISSUE_TEMPLATE.md diff --git a/.github/PULL_REQUEST_TEMPLATE.rst b/.github/PULL_REQUEST_TEMPLATE.md similarity index 100% rename from .github/PULL_REQUEST_TEMPLATE.rst rename to .github/PULL_REQUEST_TEMPLATE.md From 601cd90fdbb76f972442f3841a2f4d25e5d6f200 Mon Sep 17 00:00:00 2001 From: Ngalim Siregar Date: Sun, 3 Dec 2017 16:37:52 +0900 Subject: [PATCH 360/399] update pycharm instructions --- docs/_static/pycharm-runconfig.png | Bin 138820 -> 55441 bytes docs/cli.rst | 7 +++---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/docs/_static/pycharm-runconfig.png b/docs/_static/pycharm-runconfig.png index 0fa0810f9b741d8a822b63ccf0f07f7421fed9ea..7e2de581bba4c30f08c005bcb8c2b4ef8036d91f 100644 GIT binary patch literal 55441 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

    W$eQ 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 Date: Tue, 5 Dec 2017 08:54:59 -0800 Subject: [PATCH 362/399] Fix code example for HTTP Methods `return` is required to make 'http methods' example work --- docs/quickstart.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 7440ccd2..d3f3dea0 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -315,9 +315,9 @@ of the :meth:`~flask.Flask.route` decorator to handle different HTTP methods. @app.route('/login', methods=['GET', 'POST']) def login(): if request.method == 'POST': - do_the_login() + return do_the_login() else: - show_the_login_form() + return show_the_login_form() If ``GET`` is present, Flask automatically adds support for the ``HEAD`` method and handles ``HEAD`` requests according to the the `HTTP RFC`_. Likewise, From 2d2c0169d47a5345dada2c96b4a445c570f7c112 Mon Sep 17 00:00:00 2001 From: Harry Moreno Date: Fri, 8 Dec 2017 15:18:02 -0500 Subject: [PATCH 363/399] Fix docs, how to get db connection in pattern The docs are inaccurately suggestion a db connection would be available at `g.db`, after calling `get_db()` a connection will be available at `g._database` but even then I think instructing the user to use `get_db()` is the best way forward. --- 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 15f38ea7..eecaaae8 100644 --- a/docs/patterns/sqlite3.rst +++ b/docs/patterns/sqlite3.rst @@ -67,7 +67,7 @@ the application context by hand:: Easy Querying ------------- -Now in each request handling function you can access `g.db` to get the +Now in each request handling function you can access `get_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 From 8a5e389da06f7bb7cf5a364edbc47f0e6e7d1771 Mon Sep 17 00:00:00 2001 From: Grey Li Date: Thu, 14 Dec 2017 11:08:33 +0800 Subject: [PATCH 364/399] Docs: Fixed typo --- docs/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index 6e30466e..6ed27a83 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -104,7 +104,7 @@ The following configuration values are used internally by Flask: Default: ``False`` -.. py:data:: TRAP_BAD_REQUEST_ERRORS`` +.. py:data:: TRAP_BAD_REQUEST_ERRORS Trying to access a key that doesn't exist from request dicts like ``args`` and ``form`` will return a 400 Bad Request error page. Enable this to treat From 58ea360d431d7d2d430d08d89a75dd70946fb263 Mon Sep 17 00:00:00 2001 From: Grey Li Date: Thu, 14 Dec 2017 14:38:37 +0800 Subject: [PATCH 365/399] Docs: Add missing backslash --- docs/cli.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli.rst b/docs/cli.rst index cdb0fc59..136e43e0 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -176,7 +176,7 @@ Unix Bash, :file:`venv/bin/activate`:: export FLASK_APP=hello -Windows CMD, :file:`venv\Scripts\activate.bat`:: +Windows CMD, :file:`venv\\Scripts\\activate.bat`:: set FLASK_APP=hello From e30c39fe78cb8d3fe8379f032b66f7719b2e99c5 Mon Sep 17 00:00:00 2001 From: Camilo Date: Thu, 14 Dec 2017 01:55:22 -0500 Subject: [PATCH 366/399] fix non-passing tests for logging on pytest > 3.3.0 --- tests/test_logging.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_logging.py b/tests/test_logging.py index 1a010569..cadcdab9 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -17,6 +17,10 @@ def reset_logging(monkeypatch): logger.handlers = [] logger.setLevel(logging.NOTSET) + logging_plugin = pytest.config.pluginmanager.getplugin('logging-plugin') + pytest.config.pluginmanager.unregister(name='logging-plugin') + logging.root.handlers = [] + yield logging.root.handlers[:] = root_handlers @@ -25,6 +29,8 @@ def reset_logging(monkeypatch): logger.handlers = [] logger.setLevel(logging.NOTSET) + pytest.config.pluginmanager.register(logging_plugin, 'logging-plugin') + def test_logger(app): assert app.logger.name == 'flask.app' From 33fa58094701d5b1edbf13ddfe7c93b4c5821b63 Mon Sep 17 00:00:00 2001 From: Camilo Date: Thu, 14 Dec 2017 09:40:34 -0500 Subject: [PATCH 367/399] restore the logging plugin only if it was active to begin with --- tests/test_logging.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_logging.py b/tests/test_logging.py index cadcdab9..be4e76ca 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -29,7 +29,8 @@ def reset_logging(monkeypatch): logger.handlers = [] logger.setLevel(logging.NOTSET) - pytest.config.pluginmanager.register(logging_plugin, 'logging-plugin') + if logging_plugin: + pytest.config.pluginmanager.register(logging_plugin, 'logging-plugin') def test_logger(app): From 604cc758fb6601782745572452715fc295dda011 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 14 Dec 2017 12:20:03 -0800 Subject: [PATCH 368/399] use config fixture, get plugin via unregister --- tests/test_logging.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_logging.py b/tests/test_logging.py index be4e76ca..80540e84 100644 --- a/tests/test_logging.py +++ b/tests/test_logging.py @@ -9,17 +9,17 @@ from flask.logging import default_handler, has_level_handler, \ @pytest.fixture(autouse=True) -def reset_logging(monkeypatch): +def reset_logging(pytestconfig): root_handlers = logging.root.handlers[:] + logging.root.handlers = [] root_level = logging.root.level logger = logging.getLogger('flask.app') logger.handlers = [] logger.setLevel(logging.NOTSET) - logging_plugin = pytest.config.pluginmanager.getplugin('logging-plugin') - pytest.config.pluginmanager.unregister(name='logging-plugin') - logging.root.handlers = [] + logging_plugin = pytestconfig.pluginmanager.unregister( + name='logging-plugin') yield @@ -30,7 +30,7 @@ def reset_logging(monkeypatch): logger.setLevel(logging.NOTSET) if logging_plugin: - pytest.config.pluginmanager.register(logging_plugin, 'logging-plugin') + pytestconfig.pluginmanager.register(logging_plugin, 'logging-plugin') def test_logger(app): From 1cdfe0e5e9c1eabfc3038dd2e4b79d768c6e1ed8 Mon Sep 17 00:00:00 2001 From: Andy Craze Date: Thu, 14 Dec 2017 19:45:09 -0800 Subject: [PATCH 369/399] Fix sentry raven install docs The documentation for installing Sentry's raven client was incorrect. It should state that the client must be installed with the extra flask dependencies as mentioned in getsentry/raven-python#1075 --- docs/errorhandling.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/errorhandling.rst b/docs/errorhandling.rst index 413680dd..4c260112 100644 --- a/docs/errorhandling.rst +++ b/docs/errorhandling.rst @@ -42,9 +42,9 @@ aggregates duplicate errors, captures the full stack trace and local variables for debugging, and sends you mails based on new errors or frequency thresholds. -To use Sentry you need to install the `raven` client:: +To use Sentry you need to install the `raven` client with extra `flask` dependencies:: - $ pip install raven + $ pip install raven[flask] And then add this to your Flask app:: From 28d0f66c1f5a6a9d3f0c229822e917d115fa5aea Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 14 Dec 2017 13:05:52 -0800 Subject: [PATCH 370/399] remove tests and docs for python 2.6 and 3.3 --- .travis.yml | 12 ++---------- CHANGES | 2 ++ docs/advanced_foreword.rst | 8 -------- docs/contents.rst.inc | 1 - docs/extensiondev.rst | 2 +- docs/installation.rst | 4 ++-- docs/python3.rst | 23 ----------------------- tox.ini | 10 ++++------ 8 files changed, 11 insertions(+), 51 deletions(-) delete mode 100644 docs/python3.rst diff --git a/.travis.yml b/.travis.yml index 972ddb0c..440bdf95 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,25 +4,17 @@ language: python matrix: include: - python: 3.6 - env: TOXENV=py,codecov + env: TOXENV=py,simplejson,devel,lowest,codecov,docs-html - python: 3.5 env: TOXENV=py,codecov - python: 3.4 env: TOXENV=py,codecov - - python: 3.3 - env: TOXENV=py,codecov - python: 2.7 - env: TOXENV=py,codecov - - python: 2.6 - env: TOXENV=py,codecov + env: TOXENV=py,simplejson,devel,lowest,codecov - python: pypy env: TOXENV=py,codecov - python: nightly env: TOXENV=py - - python: 3.6 - env: TOXENV=docs-html - - python: 3.6 - env: TOXENV=py-simplejson,codecov install: - pip install tox diff --git a/CHANGES b/CHANGES index 2cb24c20..11fb7df2 100644 --- a/CHANGES +++ b/CHANGES @@ -8,6 +8,7 @@ Version 0.13 Major release, unreleased +- **Python 2.6 and 3.3 are no longer supported.** (`pallets/meta#24`_) - Minimum Werkzeug version bumped to 0.9, but please use the latest version. - Minimum Click version bumped to 4, but please use the latest version. - Make ``app.run()`` into a noop if a Flask application is run from the @@ -115,6 +116,7 @@ Major release, unreleased - Blueprint view function name may not contain dots. (`#2450`_) - The dev server now uses threads by default. +.. _pallets/meta#24: https://github.com/pallets/meta/issues/24 .. _#1421: https://github.com/pallets/flask/issues/1421 .. _#1489: https://github.com/pallets/flask/pull/1489 .. _#1621: https://github.com/pallets/flask/pull/1621 diff --git a/docs/advanced_foreword.rst b/docs/advanced_foreword.rst index 82b3dc58..bd56f53c 100644 --- a/docs/advanced_foreword.rst +++ b/docs/advanced_foreword.rst @@ -45,11 +45,3 @@ spam, links to malicious software, and the like. Flask is no different from any other framework in that you the developer must build with caution, watching for exploits when building to your requirements. - -Python 3 Support in Flask -------------------------- - -Flask, its dependencies, and most Flask extensions all support Python 3. -If you want to use Flask with Python 3 have a look at the :ref:`python3-support` page. - -Continue to :ref:`installation` or the :ref:`quickstart`. diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc index de4d7a91..f76b1591 100644 --- a/docs/contents.rst.inc +++ b/docs/contents.rst.inc @@ -56,7 +56,6 @@ Design notes, legal information and changelog are here for the interested. unicode extensiondev styleguide - python3 upgrading changelog license diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 3e8f30d4..29c33f3c 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -383,7 +383,7 @@ extension to be approved you have to follow these guidelines: (``PackageName==dev``). 9. The ``zip_safe`` flag in the setup script must be set to ``False``, even if the extension would be safe for zipping. -10. An extension currently has to support Python 2.7, Python 3.3 and higher. +10. An extension currently has to support Python 3.4 and newer and 2.7. diff --git a/docs/installation.rst b/docs/installation.rst index 370ce48c..88b9af09 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -6,8 +6,8 @@ Installation Python Version -------------- -We recommend using the latest version of Python 3. Flask supports Python 3.3 -and newer, Python 2.6 and newer, and PyPy. +We recommend using the latest version of Python 3. Flask supports Python 3.4 +and newer, Python 2.7, and PyPy. Dependencies ------------ diff --git a/docs/python3.rst b/docs/python3.rst deleted file mode 100644 index a7a4f165..00000000 --- a/docs/python3.rst +++ /dev/null @@ -1,23 +0,0 @@ -.. _python3-support: - -Python 3 Support -================ - -Flask, its dependencies, and most Flask extensions support Python 3. -You should start using Python 3 for your next project, -but there are a few things to be aware of. - -You need to use Python 3.3 or higher. 3.2 and older are *not* supported. - -You should use the latest versions of all Flask-related packages. -Flask 0.10 and Werkzeug 0.9 were the first versions to introduce Python 3 support. - -Python 3 changed how unicode and bytes are handled, which complicates how low -level code handles HTTP data. This mainly affects WSGI middleware interacting -with the WSGI ``environ`` data. Werkzeug wraps that information in high-level -helpers, so encoding issues should not affect you. - -The majority of the upgrade work is in the lower-level libraries like -Flask and Werkzeug, not the high-level application code. -For example, all of the examples in the Flask repository work on both Python 2 and 3 -and did not require a single line of code changed. diff --git a/tox.ini b/tox.ini index 45d81b65..ed8b99e8 100644 --- a/tox.ini +++ b/tox.ini @@ -1,15 +1,14 @@ [tox] envlist = - py{36,35,34,33,27,26,py} + py{36,35,34,27,py} py{36,27,py}-simplejson - py{36,33,27,26,py}-devel - py{36,33,27,26,py}-lowest + py{36,27,py}-devel + py{36,27,py}-lowest docs-html coverage-report [testenv] passenv = LANG -usedevelop = true deps = pytest>=3 coverage @@ -29,6 +28,7 @@ deps = devel: https://github.com/pallets/click/archive/master.tar.gz simplejson: simplejson + commands = # the examples need to be installed to test successfully pip install -e examples/flaskr -q @@ -59,8 +59,6 @@ passenv = CI TRAVIS TRAVIS_* deps = codecov skip_install = true commands = - # install argparse for 2.6 - python -c 'import sys, pip; sys.version_info < (2, 7) and pip.main(["install", "argparse", "-q"])' coverage combine coverage report codecov From e296a67a08810d2395e8baa2c1b82ce5508fe808 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sat, 16 Dec 2017 07:17:13 -0800 Subject: [PATCH 371/399] add appveyor configuration --- .appveyor.yml | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .appveyor.yml diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..102ff5c3 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,20 @@ +environment: + global: + TOXENV: "py" + + matrix: + - PYTHON: "C:\\Python36" + - PYTHON: "C:\\Python27" + +install: + - "%PYTHON%\\python.exe -m pip install -U pip setuptools wheel tox" + +build: false + +test_script: + - "%PYTHON%\\python.exe -m tox" + +branches: + only: + - master + - /^.*-maintenance$/ From 411b08ff8bcd30aca15f2dc0be5f633879c633db Mon Sep 17 00:00:00 2001 From: Hsiaoming Yang Date: Sun, 17 Dec 2017 02:43:06 +0900 Subject: [PATCH 372/399] Better appveyor config --- .appveyor.yml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 102ff5c3..107fb409 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -3,16 +3,21 @@ environment: TOXENV: "py" matrix: - - PYTHON: "C:\\Python36" - PYTHON: "C:\\Python27" + - PYTHON: "C:\\Python35" + - PYTHON: "C:\\Python36" + +init: + - SET PATH=%PYTHON%;%PATH% + - python -c "import sys;sys.stdout.write(sys.version)" install: - - "%PYTHON%\\python.exe -m pip install -U pip setuptools wheel tox" + - python -m pip install -U pip setuptools wheel tox build: false test_script: - - "%PYTHON%\\python.exe -m tox" + - python -m tox branches: only: From 94fd3edf3185ea20f1b3af62c247e031cd8136dd Mon Sep 17 00:00:00 2001 From: Grey Li Date: Wed, 20 Dec 2017 20:50:45 +0800 Subject: [PATCH 373/399] Docs: Update CLI for factory pattern --- docs/patterns/appfactories.rst | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index b6d4a729..3e880205 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -89,17 +89,19 @@ For more information about the design of extensions refer to :doc:`/extensiondev Using Applications ------------------ -To use such an application you have to create it in a separate file first, -otherwise the :command:`flask` command won't be able to find it. Here's an -example :file:`exampleapp.py` file that creates such an application:: +To run such an application, you can use the :command:`flask` command:: - from yourapplication import create_app - app = create_app('/path/to/config.cfg') - -It can then be used with the :command:`flask` command:: + export FLASK_APP=myapp + flask run + +Flask will automatically detect the factory (``create_app`` or ``make_app``) +in ``myapp``. You can also pass arguments to the factory like this:: - export FLASK_APP=exampleapp + export FLASK_APP="myapp:create_app('dev')" flask run + +Then the ``create_app`` factory in ``myapp`` is called with the string +``'dev'`` as the argument. See :doc:`/cli` for more detail. Factory Improvements -------------------- From f7983ae96a5da9c7763ebd65947fb6032e2df36c Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 3 Jan 2018 13:06:14 -0800 Subject: [PATCH 374/399] concat newline in jsonify ref pallets/werkzeug#1130 --- flask/json/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/json/__init__.py b/flask/json/__init__.py index 6559c1aa..6a10b737 100644 --- a/flask/json/__init__.py +++ b/flask/json/__init__.py @@ -264,7 +264,7 @@ def jsonify(*args, **kwargs): data = args or kwargs return current_app.response_class( - (dumps(data, indent=indent, separators=separators), '\n'), + dumps(data, indent=indent, separators=separators) + '\n', mimetype=current_app.config['JSONIFY_MIMETYPE'] ) From 3012d20a3f24c8c8afa4be754d6f198506f72286 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 4 Jan 2018 07:29:15 -0800 Subject: [PATCH 375/399] cleanup --- .appveyor.yml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 107fb409..3a7d2f63 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -1,15 +1,13 @@ environment: global: - TOXENV: "py" + TOXENV: py matrix: - - PYTHON: "C:\\Python27" - - PYTHON: "C:\\Python35" - - PYTHON: "C:\\Python36" + - PYTHON: C:\Python36 + - PYTHON: C:\Python27 init: - SET PATH=%PYTHON%;%PATH% - - python -c "import sys;sys.stdout.write(sys.version)" install: - python -m pip install -U pip setuptools wheel tox From c11fe5d00702622f8c31fd72ee1a249c175a0b57 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 4 Jan 2018 12:34:25 -0800 Subject: [PATCH 376/399] smaller, optimized image reword / reformat the docs fix tox build directory --- docs/_static/pycharm-runconfig.png | Bin 55565 -> 17219 bytes docs/cli.rst | 63 +++++++++++++++++------------ tox.ini | 4 +- 3 files changed, 39 insertions(+), 28 deletions(-) diff --git a/docs/_static/pycharm-runconfig.png b/docs/_static/pycharm-runconfig.png index abfccd7af173cd71ccc3a772227e0b81bcd35ec9..dff21fa03bd2fcb62d3a7d047a5005dee9ff15a5 100644 GIT binary patch literal 17219 zcmb4pbyQqU(3PG2o^NJC0Nj) zgFC^O=iPVre7oP-Z+HKwbNY67RdrQY{i<)Dj?n}w;yJP-p8 z#zW6}lP$%=$CajFQ6RLKtk_!3;1}YkFKRYQnXSCkWpn2xCWw@$TMwp0e_^^UB+*G7R zMa8(~jDr!aqOwk==H{}BZ!4R6nFLk5HKg-fVZ4gwa;ipRlJDQ?>dHt;b4a}x5EgV) zkk*otmXTF_uOe?}XQ%bvK-?rcCN)b*Qo6PRTU!3SW`vXDj}k>`RmuO<$1|1c|P`^YLHck zri!Sc_lAa&((&=}qRJj(3h%6Cq;kD98`53t>na_>GfY3$v{a{w>4%G}nnf5(*EGXg zTe59Z+e%t;yo}V#Y8q7EJNgyYs{4J>_e}a)pXjF}8v`#bsz7oHSbxx$ZfI_;YA%ax z==DsZxI8X4!8)%s@F$~k8`IyyS(smbg6!~G00Lp>}A|G6o`K`+ZwQ!2i$ zAU!g#vBS^LzcAd&-%2M5q8N`XbJb9#{GB0|I$BxZQjzRBIfJI>2ZJcf{HkXOG*mas z|7KzD6bg-UFKx}Mj%#iCn)2mqP+D;1}l8}sPP zKH}^pFplGOeycLXdoYr%6%-h#HX$Di4b^<22~s0^lg~BrQo7`^hvrq-bJns>*^n96o zxFa^fMZ`}p`%x=bP7?bRUvqxbf4_DiXy2Ntcf4Evd$P)E&$hvMdn4#OWr=rLlD2`ZO9a&rd0F0-;W& zoEf~J^5I9^v?~X6-bB5(65;-Qa%%fGKI!tD0pb1Wh)bb;I3$zQ3gl3IRW|`NCgN|f zJsr2}6`w%+tpA!Ok=w_vNrW1wb9a2+8VduRRs=urs*H%8hifLjm-#a8&_gG@7mpXo z^bVXW@oBd8QMaNK<)ADVf%OKg(*2N>s2qVkh}x{BI)|8V2XyTHFf@Egw3xdDG0W{6 zZlL&e6K~z0_}7W)QRxDPfQ|^UbhCpqc&8;3Ko7Awv7j&8xpSjvvPM1?L_VdF8s9E` z4B~)MRvg2z*p8KPs?7V30ZVt;&n{un&?~A0>ZUh?KinrEnOtmU>aQ^?e!I`1uj<7{X?FEu zmMD7#Y~!zXE-sTbBw8>2X1BJLvXW%V1pGk?ow*C$R{x;op0buW`f2KsnH2`xPq97t z-pYsCG~Og>4{(CP_8I*b*M2fzc)~SK55?6^O|)gKw}R1ee$gSG{3k*~2s~t`dzL2t ze@Sy;cM)L>LN(Zs5zQ(ekBCisH~Dslb7GwL9r@#kY`bb^Yy}^>y**Fa`8&aKX630_1ta zM^xoDR41Rph*LYX)^=On=i=TKG!Q4W(byhFgO0;5B-%MXkB6Ug)I-udA?`nyVFx4A zbe+pU&r|_3UpQx}b~VTNz%&Ju1=8COM-wYs!&W~hfFofJrTbGiZsL!< zI`^PVZ!+NP>!ba!iuZqGfeVU*w+QhN_uXVR^`d<`M9rzDn_qgJ%MVmLyCE|6Z+oD+ z*B$TDT#7OFBbyOJLw6*WS&@Iq7a-2u+tg;M{x_bs$X4O6IBm$EG-2bYkPBh*SeKJR z$WRF|#nDWFRT@y8ny)AH$N3F8@$%%4as3&-4On4KRT`L(goMn^<_skDeW*seRt!PW zZ2cBTU}F8HAYLrW*~^#ZG%m5>(B11ULi)Bk#?WFm+N##zd3E@Wt&qx$&|ZP|VFxz} zhO?0gk~x+(H2EguE1lDDCl2gR&gUf3S!!7EnHHkCVi?6WGE#Dy{$z`P;>|lyc*lekokc_)|Wp^#c3v9UVPaqUHNkr_rPXB-@ zv>6uX758iBw3W#t+P%GgMe2BMBvdgLJoRwdTjBx5J}wc^DcE`kI55T!FM0V@i(I5p zH(H7C{%nesAF3N3<%rc{F^nyL6bso}l&5(rzWRl613o(EjffK-=}$?>5Z*88m$aQVJONbM}9z z!RQ?-Xked2enYvSp#|Fr{qBB#y%dsMpvn;J_9jd`tFF*kj}=^ zsalORd#xkZeZYA)1SWKm()on-{E52YN7nCi-Je3i*=qcB1%IIKD-fuklxH9ZXIKx! z=p1iZvlMaQ(oOs&**sMB9ZXP1QYgj5{b%D-Z1Har2CwS*uuTc^LLSpW?&ayhWCC}$ zVk7VHFNBp}4}}eVp&9(NeDvDtO$2e=^K}Sk+A!pJ-bi0Sw1#l;^DgCeli0{l(qrTd zs;A8<&>eXRSB0=PeimKWmez!LB7PML${T20un4WZPaK;Sdyb&r>nauD_81-=4m7m# z1wB)3P&??J-R_T8`~2IybjZ`E@l&a=@nzoPTD!IvZ_};rM;D%#R;&S}DJ{o4ZkB|m z=T1Xs^M8z0c+LTgAHcHwVBBwUG1ceX ze^4Y0&f0N#+feuCqES?KRsFJVr2sdZ&eJkcb&`5-e45_!DVmP@ofE^F7qaWq0McRh zs3i2f!?+HH@TQoMS3yL{s|6jq`wk%eM44lJeE(X%C3v@3 zVc2ZJ(lvAFn~3K~+)r$H(EV%fiKegT?djX^9gtkt#jsJ*+>0b(@lg@)PlDk0@7xTV zfu`k0?-21mbT}Wm@#I1(<`qnFuKY75xicp)dHJN{6A0c_my+{iCKA>A5|XsErN}I# zX(xak;%eG0<Yn-57P$X+_t^LhN?(zsZdp>wyM>RW z8F0dsf;oqszudI)m!5nOd4r31YnKefD6==8_MU`&xnQj;Z2>^{RG0`uBYK0L2t`A~ zkfj4f#8S|m5sB9oC;5Jz(B8ugo`_HYRY`=Ra}8&9-5R4`gs@mI^!m@Tdg;05?Y@a^ zx)7b+Me}z7)&*`zt`gtq99~f0c{-JD zEj3qDEFXYt_pt?7z{@I7rIuw8!Kcxg!{9}{|1x53)gOI#w!a*5StoyX`e8vm_GY)( z;wEyhx3SZ-pj8L57vh{}d#Wcrj#7hywD(UvM(yY|PJ%aQkeP7w0Z(GYrPzW9EI~)t z*q#yTO+jIVPv`BIPzLC!)%DD1dF``wK{|5wslXUpTKzbKU|C!x{h6urm)3XZd%wMp zn%jsn;*T_+b-F^tMbm{caxPf>vM_57pmS&1C#JaGo``!FQkd=|I8%Ub8!@*(iA4+x z+$2T;!MMPELq=67N_`@S;tzQ5K3Co9NY_LB25j1;`l{KW9ZRI>fMG)aCvcCQENbi# ztySQ<$0_)Y#^R|p;&wJM*C&i}>E+t)FL(kosLNkn1i=gv@njo_N;GRW4{Bp--Gb)} z4b}Qb*WvD4wHow`WpByt;r59!+#;MOYuI1LQJyEiz*c4+f`RwpWunvqEkN)#v@48o z<N5X1@T805Kv|Odb?#SDY7giu#4Tmc z6ZchoQ0syj`PZM#)5Lr zx%zfn@~&jFF0mHe!N57X+n%(_7F#;F#u4EA?|c!ej2_cw1i|hx`$*mvKyp^}V2tSt z_lFY#iin&Nx1JE<3Ksjf49;#`^BCoxI=vBwasCJD3lmcjo-QO6f8^xLh>Patg3I2< zETdoa97QepH=iu@o~rSerVm{d_yr!AObFgwowcT~U875}4>zP36tS?S4 zq2k-Fz*S@<&{o*4!$zC1%5(AK1r$Vtyb$+M8HVd_01<-UFEbUJfq@t1wWkGd2LlYc zA&Y);`;b3-Mxo$qY&gN=y<13!XTDd9X5h?)-PkFasLfz=e@@JK4)nN3xz&A%p+uO& zlT#XTTfl}e7JbR7-AOe%mhq$!m^-u*B}X4b6pb=&A%mgo$lp$1z^(XyPQN1G4j^cb zj+TTu&=vd3FU0NrMbpjpS7_lP_%a>{!ssvJJ2e9@E?+0Dk9&bym0HA^4}wMTA@Wxr zcEr@M*O4|FB=fmbGK%2VdFKnTO+BBGH{mbA{x$c?qpH_1JUXzU$70L;6k&0&yik?E z**SmLrV;;mG9VgUqglyDfF-vV!$6uEw+l~51 zD9^kNYaDCkO6fW5g2@Y;d4+1;Fvp&kK?Zhy&M%D+7%u{}O93&k2V&2c265Yc;09_{ zOTgwoYZ~$~>$4s?QgQ?Bv`2A_`W?834xJJh=|FAM6a`+gx>4j(5~7KN_Ml4OShTIL z(Ez`zWYo*c!Zm)_EgD!g&2%7%dW3VtLTH}MS(x!BR9NxKsoI8xnSuF23V|VI3Jx3L zRg*}Q{|ka|k{}kC0qKKHrbbT-EOo|-6(*|a)5N;z=#NWi-k%8%&9&6Fe?ojVg^wkl zz=0~O0$+*1HyHdy;HrUs$n-=%jZNG8_Zi8xkf3~}pT}yw)`4m+>>AsXkTpq}@7u@$ zNU#ICavfppr7#>HY@7Zpx0P?vAEp+dcQDdiSK&)Tfq-n|HJ@WEo7$4K6Z#Uv6~1pk z8-A<9y@aV$-93H|&b72yeaNoXfV=Fks9D>#D+*#vb=cZ2OhGeFsyPzAiX{ffXR2U` z+m@UKn$gLLO?AV=taBwRoONVc&o5t=U>4p9Y+9) zC&z~x-uRzpa z0q%c3G{}}2K*q~pu8y`0IlL~B1%v^niDwf#tKFNRy7@-E#UJ2lB^4*q_Z#SMwJ~hI zq#X*2Ba}rHo0&Y6Vvxg+<9&G7d|GDOSMCp-DBpsoYxQ!1!q#N{jBGv2RV~I*M52L} zEJsB#-O`duHs~xP*E0lpw}j8*p0%FT%t5BQlwuD>@t4pA8qTsH)Te9wg)~B2lBf{z z)`&cS-kT!J`H5V7_jnA{U0BILM)gXdaPh^)FT~#T6!GZOBjWKoz?iav;WY}@0lOpM zq2MGjRRxQVqg;jY3ok*&>66xAu2+Tb->ZT+$D+5i+8QnQ_98a~UybD`xRUp!Zm9Np zAy1}!T(1(|J@z}J5iM~0dFc>?wF31UC{r$+NR?eiYOIWJ7|)HrGOO=`{Dm+PDCx7^ z@?=To^KcB;$dLMTmNLXECcuTw20+ISD;Xx9N<@X8n15xB92(d0Zx4c01*uGmR6hGI z^WR!oI8$5D`11-_6L_>7^sRd;@e>mxLOeeI0-G*+Ws1P7?flTav)G9cjG;4ze^T zX|}#S){!*k%t=^Hnd6+*+!_>o#f3KoNtykeGT?ojLyZ39Ole8jsj|aOHDKp614LQJ z)lVro8(&$6_!S~VKKx{O1+6Hz9vu;)wtq_QriZx|o}omUFuef|+u6GnF%BNdi`o<- z!vDSl&9xU*Xv9DSKAmHQb1sn zl~S$XhV^Nv33_ojlH)yEp7%|AqEJ~-CX(WR1j||i zlZ@laN`IhX7CljZ5VZFO^#{@?D15gIlV8bj8f3xPbJrGyXGdyAgyTEN#v!MjolU2K z0Iqm#O|bUO8nE%aRZu5aBo0K43#oOQ{3TZe1ZHtIROFyY5bF`_xpxj7?g+^@aL#Xm zKx$^BsYCZ8P=k>#bpmnC z8Wx{wSfyvZIP_|+Ul2``$osAIMg1@QeBpvFf~oWE;>CvzHpA7=U|EoL6kz|L@_r2} zuO#lL+B)g-37o9~Qxo^zQnuLD6Lz+~rm4YgsbCye%E+PAh}M4$PQ{^jH#v~jPW*U7 zQiukRY&8xCM^Vt^%+~)=3?@gR4;4v<;g`8s01~wE?7F);;X~YRbzD~ONZlk42w^jBqj~7e(lM9=>lg6{X6p`2tRNK(WDlea zB*^UjhRy5MWGNB8`)@(kp@B27rdQdR=F-9R1ReIjKY`I-z1=l)Q<7XJff}IrtF%D8 zCUsV*}7je1Fi&qv;=($ht#-QNU(vIee_}O`s@`3x~k;XWrSKv#TN~z6&7O0daBlRGs zwL_2!d@^4UGXc4b#yPX&94X)jar8ny{%h963@o>*N!2rl_Pk_+b!et< zXi_+sO<0eRlkk!Vt}9$ord6h2JK1)mc2o7p@MzhF`FBiyy=*`{PIau@KL6kO67A#X z52l?;f1cPveuupKthJX?ArI(t^SfMtPDJ};VFZ*@$ zBisTKDJ|oE*QR%V=;9Wn@`<|m<80&_A{)fcN~D-XEUB)ZWWdXlNd`79{2bn3WRYFy z;qWnA-QGae&S0crez(d%@~(6$?d-isxuoA{^FcG==DB+_m(kwwalrI`>jms2$7As% z$S+{ux7`%ZWRtbd&jYX{Vlr(+)Q6E!J@!vS-4xzlOiYnx^})@@_!TeQ(^FuB&IMmQ&>UWt z);|yTRBYRvo~oLApCqdLwM-b?AG*N-R3HAaeDMN2ig3U^seOYMa3{U?l7eP5NsSQW z)03TvjydUf@rrIHIE<1(ZpBaOZsVBs zq5r{;gtL7QBxEc0Sd;9oXVb&1lK2*XA-)@%xp5KAZj+Unqi9jA4<_LoanH(tqyar@ zb+G?!bD)lILIgf%pqdBytBXxCuT$>Ii%e`l4ZUhnhMBKpk1_=xdj6nHmxjkR<0GYvD zyl0{;JistiPK}b4Q^!{xDHWpyyOSLg%g7AAJ{w+sJ?*bOF}w{eUMEc;0) z!C3sU%%qgbdy_h4YA0^>7tEFVrqRF3JC+Vowky7m%UxUC%m=Q@`HnmjY7jT1zcRx1 zP3#C+XG|>C(l@*EoPJuGvL8+GmuM|1kt*3zTY3S}ngKCX%ZBxk0hb4K6QLX7YFV#G z1P(f7>mz)AP;{BbEqr~k?nL{U3{Ay>?hVBS5~ig{sRr~qy&&ottv}N{)hS|OE-v?9 zx}CNZPP9VGgc7q`a-8O(@YgjO2uf8T?-=x+o}*)MrXSWO8yhRyFu`A# zFZaCFp;eeZSr=%1EKc*mlv|B3OJg(?Wq;4s-};EcBnbSj-;_LgPuhWwORw#l40T9h zu_HlMhTwtj@GGBW7fbKiry?n%{IA|@0@KNsg}btvp2ekVCLy%Xc;aOVb}-HOt14`Z zZNEc@I1dKu&tfr|Z+CDR0gmkXm~s_dcke&V`dkUKBkR|ZfBM}})1N9w7gsMdi(cO7 zaD5o0R`nRFtX2grRkL1O9k8-%1%9d!-V3~9^ZtPP9ZMF7q0JND%_M7^6%7=n&)2ru z+*HVDYT>hI(70miP_>x~1bl#aHc|iz_rESo&~S@~m>#2Mn0_g-?i(5w@l_1!z-VX_ zxWV3Qc9<-t8ba#u%mnt_#U%PE6Y@!Nj90MNVOCe}bTcxu3h!uzuRv*!@JKDKvGUAi zgx+PTm75d}j!yn-eOzQpPvdlGQK~KUC59P(c51Df#OnE2P!qj?jVB)(n0Xp#Jt$e% zDYq=_i8wDbwRO9(GjzR0MJ|X)DyU(@$k3sv(?`qkCJI4EY0%^1)?fh&9N^vi(}PV4)V>JmPo{4J{osdd!}=uDUTz)*V@&cl zD-Jw53J)xbC~jV|W)}i`!LYO^=^o>cxhHmh`gJ(vMiIRZIYwEi`6#Fb$rmpX!X6s&P8iWxQSNuUzEWXMm z_{qQLPsQPd8$=)Y=W*o933Y#4vt@R6-N;XbG+1ehuJ16?!6eVQErI7troB;bI^BCd`w^^6&IVClLAR+jpkTU zeNiNB#sd&U14;MkF{c_tH4}*~;cv_(qBC-`_;6w;Qp{U97*W->p{nfcVo=|o52FVY!Sfj>1)Xhh`(AY+$nd7VdSVV0@}c9J2is%RlDN_tVzd{ z0fVN>2Jf-$*|YEqW>btG#I|q=is%ikN@$n~k2sDx(l5k+{EY22Q1G0N>b^QkIo+tC z{q>q3Yr5@oX%>I&x>(w82_CG}I^7Nmi)2Cb6Fr7OYK_8)t%WqU#j;ZR0d2;@3b@(a zyafaTxdgoGUO=jNiBNJCQaC?r%!Zq-*wfU}y&7z^Y6@RCtD^!7VZP(Ag6Y!C^zipW zkVVM$*Jb_BygS!UZ{H?2X}Z`uF7?4WDiERnR`>n9%H$t?xLHOe#hAm;E!wRuqWA97 zU!8d~`J%Ekveg{^Wv8_A<>x_?vhnN@!HcoLnt?8gSmb=zaP~Xg0pxKfK{v9UQTnXe zXyY3b;7a;dd^Y*nE{AZ#xW3Rh_}`k6`S6E~OV(lj;Xy~l1TBG0L4VN(=y)ar9?;`b zdYfi)@V!C#6Y+XkeiE6@|0U+9@k8oO;@pN88PSY183O0e@*{xredf>D#1k=DL4Q`UIUUj-b3 zfEHRIbu!6UdN53Nb=(XH98$VnoI`~Mp{EYlWlWq>i*0DZu0Q^q#$2}XZNw&7qX7EP zo(P^mL|`2U`37|KYcWoEad_*0p7|4{kL_pgF|s!M4rI4duR>4!grFbi3)Vkv$Lr<; zrU$=gDk7&d8xB_+>eCwoL=OD*xZ}Gp+k2!9lx)kf6*d40AZvl&R}&*JIDIWkz@W~K zhZ;s9qL+P56S9K5h5XAV$LQcDzAJ9&&!!>Z-gui@TMS-zXEa?vZiy9!aRbM_x^` zwKcEb>IvP6s=ZAF7_WEEzt3B?)MGE{$tj5&LYNm`WO}Ay+M_ey!C|nk38J zt4mx~#$MCABhk%8kDstiU(3Gvri1yVsQFC)piSO0x0{X8zcf&aaeNJ7YMjY32zXlI zxK}$j9@`!hz0p1kARQod30%U6BX~Rp89B-VLJYQ$`#!^vU2(Aw{jk18S;H1M#`2ap z;@AjwZA5WZjv_|y|9#WZ9=3bF8TiwiCQ zOt9fzqFekAZGUR$+}@-DH?A}v;@R+5X~eUKy?OXM9moHlGzXtlD8qnPmV&hluM%}B z1XU0W`;43JV<<ltib%RZ$(+P}=4^$FPx@5s0O?nlPrK<0J5D(!)=bal z>BEzG%WEDywo*hmb)y$gw?=B!G{am3LLkN}kqXB2Z5jT`n!V-48;jsb*-!f&ZLZtr zT3hlBrrQEHGZueGKCHer3KaF1L1woLIyC zp4!|`p*yVn0q|-smqOGDw|mY1mFI{bTy^pqP0RGQDMs~SubY2|rDYaY=qG-MlO>w# zDT_xqL@&y=(_~8U&tJ?P1^5vz2AmdtYOv46dH%V(Mk7G>;El?~XyVCcjmQ%fHkS$f z_SNbSxesrOCm<|^=b9xYJ#9(Gs%(7{q$2{z*Mpd{@@c5d> z^Gc^zH#cH+l;g(N@rl?h&!@iOr5j;3QZUd+l-IIIgDyRS8$+TAwK|m+zBt$7%5`y; zY0lGMIAs!cnr?mCi>lhtfX;o99gk0h=ifF-FtSg-15ocRzw~|tGN&N|XQY+%-5Tp> zK8;!gxA@FUiv_i?r-*eB$MdzP+HZyb2(y_nhsLJA?jf2yanq+X;MBvC28 z^EWuvb6`>yQUB1w#(K?w`{FWdZIeGddL61oJ!0a-e|AYn)qa=3Dz2}x6y+o_x^1p* zSnE#1*1U429DV;(cCVLympHYT{rVkJIk#AJ3)!e{hfWTtVRDuZ4=w zC|CvixxL$}y}C3tzEgQ5;yHZGuurHBd^1H7Sqq7D&2q_`3=9e|J6L~sD!?5o{>ot! z$(Q9!Jsv8`VWVG#{&0o;vM+-vsh(5Y_j92MU-J9#qmLsze_ydZNCfl#B_p@^n6fHb z?ftHBSAE6=(VO=NS%Vt*b*$!25P(x$mqn%kq|~oQvVyZ8gq_2H`qXD&jVA6KWHa{w zSmTAFsEBc7aqI|>lu|QpWS|x(&=mYkku6EQ@|4CR2~F8pZhY%@!2L>?@z6HZo}9*w z7FM~GCx;U6DOZ9=f*quE5v_x?4M1!_9N9VN3y7%cj}ZAm);v3L=D|Un>(dJM8XsRnnKG)Kb%x-8tOwHkQ zZ*pSCKNZ|!U!=@2D>)1}?&wcW;r7Tv`uU`YR&+dt&KZJJ8*Uk$x*+>gnkQqtQP(sw zrXKyc4lnS|3SAAR8u}1x(?ua* z!5$4F>ca-$+pK8zbmn&!H1GFh?1k(s^4ei7xX{w?HN*L8=-Pw|WS%sxD!)tn1o-EY2|LYt$1+$e#nP12M1Y%F`>!*Hyq zWquzFL>Hwn{$eMjI{RfXnK3rv5keFjyvh%@{bA<+jnI|CwZ&tZAG`{UltX+|Y`J%% zK;%Q`MuMNYJ&gD-^XDS?+_SJ=gT095qcC(qYut=f%1sdB=KQ#Dk<(<3(2tVDFEDlu5)+pBmzX z;cpVHe=2AtP0})BBW$NXe6lOx@9cq8h}l0)xmqP%hQ@*&(A`t83gsw^ezAZZI^SwP z%tfFfY^VO2JOA!I2mW@BM5C@teM)HIo$zyE@w$H|mnW@;P5;zgl*opqNfZ{+l}dsi zfqzy$5)qQ86j&49v=4X#stEkSi>a>}45NY-KYAjpj=@WN?3IO8sL1P0K@|!txPW$S7H7iuyZh>EPn&uBbSOdu{HDn^Z@Rrxx&yw2Hfa0D$()-Tc zZdrUh0<9|^`(%T<<6GtM^^O*rf0E$ifb#fg1AK)=Hmjaa{u8yl=5V@^yQQOwV62suArSS;W#>OGs2v#8Ls-*GxmwP4Z~pwaaMT~+#ri>nd^CwOXh5jAgz!w#w#0 z8~QKqfeV@q*U1YS-jbRzIuqpwdylm$LUX8gxl3m%B1qNj>WO-tJSUGKc~vDKGe6p^)p(NX)G%a z=4#2pjr|6o>aIEd00b?3B-FXIn@8wr|qld`c+d~*MeR-DtmcQM_L)13C zqjJAQ%m1wkXRk@2P2pPIe$4+A@%2FjQ%-JbD{&IdWL6|#Ig7YNJ5Eqvh0m}r9e>fA zzOovcQn`kDBAS;OHm^N$(|%ZVAF{Z&Xn88@tBHMk-N;CHHr*EnSe8A~|9iCE*Y-nV zgxN-0w5`1Rv~ooEtF_4jg!Au@%QrBD$g6i0zMnk*%__;vmHy$hFo_j*=Yans(KKP# zz9Z8LP*CAzDh4Vub_%DSE{T_kj$qP?m_{7XP$j>K7aIf_m;QUfQH*r$JuZm*0*WLw zutWKyC(Fo&f10cJh;~dchEpdb4__D``3MmrxXeF>1;d*A07y}~QrLBiB0_@1JScqr zLm$R}#qj?#7~t)eHRfZ$`EyZGZw=TO!52@U zL9f9tU#oxhJ;lnDSVTjtAXmXXs7$7#n=x2)s61aKczRK?uIyuZfoOan1^7M;P(RRq zhETVOHkt)bdzqAIs5LhF9{77%oowZ1qTM_R~0b0yBB_xU=ityXa7-g1!*z*~h92%e)ljtD7*iDp?y-Y?=s10E=^ z4Rw8Q+T-uJiGP2crf5H|f59iagV%q#`!pl>RM$Bz(+Hy~EO!^0k7V#@j;ZuE zQN$NBhwt&hG(1dERv&1=LQbhC(8JSup7^4(zG2Q^h6u{N_7m~Tj+SN&Vj=`M>KX;Iwl6txL^snh`r}$ z@kJGV#0v;G7Znbi^J_=?7xLzL>A)Tch4uhyxUch%dkfw!bmMMy>QVQskw-xB_BH>l zILxV(10Do<1-|dlP-R|@0KDU5&rx4QhexmfvMPgH>rg%UUr2P&A7Y0~7zP4;(qO*Y zYkmhK_&IblXq#x1{;t{fbxuYQn?9MB2H$M|tfPk`({$&~GP9_0(`0>t&B&bR+lM#E zBZ18%2|$kUUed|whDKnY<5Hl-^qz-vZm|nC+g>BaCN8!Nf@F_*s^tKe1RKQ)KJiX$ z0%Gpc*AIL-%;$~70g<8R<_m)<^3jc~0={Of2WQWsvIai2U3C5_0d5w;u2qEq!W zWf0|VJ9%x1eOLkv*djd`pb}XHYKi*f@VN>wK0!cex$% zGL?5e+jzKW_u3sNy1Yx>@<1>7CZrz{5X$gE-64ayU9ar4BJxAiwqPJ?H@c38cO8FZktS<{?J^s z^VV(rZnb1YZrH#uDQRMbieK8@5}e`dZk(}n@HIG{fYRRu+Tdk}nPi9k&B~Dk!j9JR zH*>PnSZ3qCdlDvRsw)aD%mu5_cblB=OBu^vn(if-l2>rJtw)4|u@@03}kBzfo1gJC9Qj(hrzdWCZsxesa&`v318s(B1 zzyq(a2MMd!C-@Dl)fq^amiFy)nT%FO(N~yUNYAEh!^Omq+?SByt6}={0C$_X=gNY@ zYO6FIZ)z{FdHm5O!@RFDMelS76H_c z;?~gvKq?3~Xr$KGPgCOH8av-BJ7^^zx_Jv;Vh8&-P)0a&4&eV5BKOhuAWzgf@3+ufF^ z&%{IL1@CGVHrbFRGU=aU7ThN5t~?jsl7wn@)Xomo{oq-!o|}a&;>y zj<-HzfBu!_`t5!+G`(2lYZ8tk>ECNe*SH*Y|JNO$SFRb=Q^bgjYZp*gnL0ypD=$!N z-__a{KzMlq+JoDowl-ZnJt=!U0l3Gxh3Ns^ltE@vfIshUDFi&RrWAn}EY2Y|_ea|) z;#sbdx}_vlK*dbUX^(l_vzUN z+(y`jI*Y?!q3B`&=a@H@g+`EPXW(RV?S-_}9O@ zLi4|EQ&!gR6*KT zs(Z%4(*q|vUqM3hD;OO4`(sB&801k#DT?1g z+Q!rB##{P^Vognri|zqcPzT4@Nf6GscLyxz{t5iAN<6GyNN^`nMTS&bj&wO0GT0U(j$(q^PKf89e|hxV-oQxW9Kp{xjpqK@0`%lZzW6{I?rI z;oLzv6dY;4Nl5a68v2qVB7FgVGgkHi5SLGFN#zKE-2vbhfAc{(F3h;58!!Yc9d{kv z%{xu73z<@c;lD_T@=3QI6jFaVETURFl`>!w~1I`1%mhi&e z`Z=zzecT`9e`8=|5N~E+25~o1VEHc>VILY}i#~?M=JB9ktXk7_m++RH8MjRzmay^R z+|ygy*|C|sHn;7MtoW9H)72#0<6?n(B$%nep#+=mrWgIQ$E)9CrWl?1^NlFV;n~;` zv4f|pwEbUefslvmHkvN#_rX z7t<_aVL0$@*cfvU4kEDy#>$Xn4~nmHcC0v%rJv zGB?LvXLoa-7PsM^La_toweGjd)o)!=cx%b1sXFRVLzU5UN42=Ry1dDDU+IQiDl^F$FA#jozsrf z_%TKC+WU#uvz>GQc|1CLg!ySPFA$+I20OV{W(rH#$02^0VTY^EbMNgU_?+!<5P*Kz zNmXj1nUtP6AOd=s-J0xshWF|xMFpa!YO2~4SDB)1iU>srfk9#L^6{7i3kp`@IeERD z3rp#D@ST?3Wh3}-^9cfgu ze8(8m1p?pGpiasw*FY6iS;mQti-K`dHAU!a><78GHLC*6tiKWFeNfM$>7fcb&o5a$ z#c$&Q>s~44?AA*{P>d6x&rW7)UKI>*xDR$DX50{NlNvbRCKC760=+C6URJoDBt;$r z9>yFONkm`?#lA~uTVjn<6G16wv+fJ(`ST0`Iyb~~Awnr*uP~%X9>>GXFIy8Yf#%yy zv#5evKB(W!*u^^@dTHktr+Z~987?8waSc-8a6+E@tVJ?cdkS9RGCZmaNp%Ul(gUx00XNb(Wog!F18J|3>wlOn2n`V8>T8ZtIs-tnKH*?U#JVD6J8Dyayk`q;eD=+rUaNc|A09+c<- ztGt>mv07i{zjXt=Ug8~?m-XHZui)CvE9zk7>J@fl&R{T+KG?pSdZU1LEk24zzWKTA zn5ca`NflgtfDLx?cjYG^;rg00=L;(6Sh82qVpN&Y8%oWQt*>f^6r?av`f>6HBQ=Vr z&AUc{wL0l+JQQ$E;kNnITVF#?f!vL-LlgEXL?xF!jr#ZYCOXK>ZCp(k9$Xz}>6E@( z(q$LXp;mAmA0JLs8>@8{Y9}#qCr1Lcv0BXQBXcY)u#?Emf8nBxwk$9UE7MG!*X4qE zjfzUGW#8|_^!OaB7k(>5AjqK|rVLKN+cp9qxG1Km$GA_hQq52w{QeVqGGi#k%e7fZ z$4+ojs~o|($a(7)yT^5>-yQ>t%Z~#yg-Il#-}2{)Qmrl^8M8ISt@g{hVIuFb?X)F5 zmuN4n{8iu+xOi+@R*|4k9nyAU|9%JGbh|EBg>~8?gns~PZS%#$19l3>;(B)|1qO(A zidOY3`<0cB%7*vx8z`keVc(S3Gc|@t3grCpAYXSbm{}9b=<`Dgm|F^aRyqaj}Q1L z_FKcDY~kZJdkBe4XV~84TV%5Lh9R(-%`)zr(CRYB8cM+&VX8o=V}Y|lW*xIGenwQx zX0-%HtZ0h|6IWm@{$#*I+WECaF)U#PrH1>j^pN5*KL#{rfkwzmW@G?pmxPZN%)Ir! z=vVJ~8zZR+CrXA#7iAH2>dYp{k8WWd7$Nt5(G(yC19J^IS4uj^SoT-SZ^}dA%wJ$8 z#%Q*Wsfrdt_1W!RNz;opNwh)6NKEq#u*#V1IC`NUT<|s1WrK^&&t%ZYs2=GOrjq^$ zx5$D<^^K>++$mAYE_mjySDmnIJE>K1oIYaX9L@^3<}-7)94O^{g(`A8Ehzms_G;)i zs5YA6D(3A3R6evyO8pc}P!@r5v?g}hkYAWn^!(gB5k+<**riZACb&U)D4Z zr(N6{8>E{*?P!;L|GItg>m65O#v7h@>7*lrJ6UYoJC$rSeoHHkW%GO&T4Yd}=I{8^ za)T~y@0O^to`7xZtivgSr(F{;?0^@BRClrJ_q~r0Q!k`1^|;T#Opd)U0ne`kXq8u9 z@$jW;!|RjszzVQ4;mTA!|8Ts*48yg`k3Rv0=`Epb5bdOL_WQe;eBCv6@RhHvgNi}e z-4#M^Z(G!KECi-2>-A;cXY?BR6c)0DOEtx)ot*E_CGUpwlJ*3aoYpKEOY;4|6VI!B zwl+M~S)Jn}5oDUZ+2p;lx4BH_k6Sf9zBzwp@m5-x`KYA^@wfyXBkj=b!c_!oycT|0mG;Uxr|s{#e_Ca%uR&n5afGr({0 ziuWN%?#AIU2K#aH%nQZ2STf+&h&OkN59RHY=WaUWguzMQ>0&3QmGkf24yIi38ohVd zIR9a?;3gc@13YpvvW7}(@y_Z0Nkk%Mz4WBGPS5xUqmXo{iKV6M{=3g5A=LU07%EP= zLQnrgPC`*Bk#KgOfA;fRA5=)qjcV*0g0sZV{}k!{-AD6ZW5NGHMZ&+E^1qb-sSE0V zE0PFD|F6dX^XAj){7Y#JRg4sXVpJ=TAN?hSv<>qlN~#1U^fz1hR)V^!G$9nIM+?t& zw^BHFlFPw=WsKtUNWD-q7OU*07H(i*hUqEdzx+d%zmRAv;`KGa1E_hTB%z^wE6vSK zLkslHf3X$8lrrJUL{}@Fo}0N#k_*ZLzI67Jk&`RwaaLIe0_}S3YVY6H*N?h^YqTMx z!AVG`Y&d4QKko0@SY%uGUtD#?#X&_y6;&x?x!ZX9Y;kj1_26hH-VF_eQ04ZSKd3*! zED98i6QYC1=z>6zD_3XMWlPX)pa``zGcE@ngp3&)U{p>mFeHdHJ+0`RjFa;BSTUAH zxta_a%GVH4U&D7XPCPddF0R&r{8!{1W5`!M-315X8||0d8})WIEtReiW^(&=#?>`h za{weBlsgxc^FIhg23I>B&~IdRJnLCR4D}+?yaR}k__1dF29pA z0t}wc)+bRxfyttXoc)9v(7EmH(Ui*jNN^D1I`r}xJjo*?RFtu;S^q{6UJVxH2ec9g zy7A!#d*{T?c4$Rdi_Yx@b$WUNX9<#RBee$2W$W5_JnM5Zvhj9w z!Z(X<1rBR#Yho_0lvP+yR5X2jGD_#=*%O4>cF-2~&HK=!YJ{1RlKT)DSKQ8fDY;Z3 zr{#FULi?B39Y$?6m%N`@1;E+W$YB%QOFtaCN)Q!hSMo#NOeUtA%(nDgNqLD5X8 z=Kje^GWua(Smd^|v*Cx`J;05DEIl6;((<8V`^Tw9nHbuda{pGPyF-Cyi&4Z1pum#& zxPc+-Okg3*o|c?rH~+$~ngyhT(@k_`Y$TrxSP0v6tRIaA3rF zJ7Gs>xuAVEhNqfLHL=8AC6kae21h~F!z)-)Kb<%k3^V=1z(Gir?MIdzz=-yg@Y*ZCJ-82XcQWptc^HfEDYYzy5Ms zj3Bex1FLr38tvoc7aZ~IL~o`i&T{D2%A%0^||Xt zKYlfE3N^l$2taS_JSm9{8;-I%W}25R-I2HV6N@I0F{B&Q<!qWhg`U26G;sK~z2qT`(E5U~mPVB}Cu*hxOT{q8QWBykgh6-qKFeKx8GZ;CQ zC)fm+xkoVa`rDq|VK@WRF;`H^-DZug@Ni8OP@I=@O)VKAGoEgUA)0?T>LUbmLP~lRZxBPsj!X>Jm`a!@-SO{#QI(-wjj| zll4hrSaL&%x%Joqu6p z^qAf#)p?_Apw{IY6ZySk<7@v3?gq;FO5!!RAaZZ4Om=bqgj=8i{H8-OhoD}dCGW*A zF~^DZ9c?~L7>kVfzS9rTaIxekxF{zy9Xc6TSTXueWsY`|C`-6nl(m4r86fs7{scx^;$*^_CBL)`dS&~-R@%VJJxLYhtqB8^#!dS9Pud`(k#pS&jqFt zB;UIT*W=ue#*2e|{ugVS0UT+?R(cPOWZNFYg<%G2%xJFnfXs`V*Rhdg#?AI&$p{j1 z(l1l2BUvX^D-NG_`pa!Xb_2V_LxV)D=&ueG?HuhDZVTJa=24X>h!*pLWY17Wd(e z-<>OKINmr?t<0|u+Xe66u5{_1qDU&eLRf!Fc^+6`4c@aWFSs&4q{faAYd`M%0BJ!U z2rSGrlI!^zBQi-`Yf5loS3(pHnoAgNCwbm1kXLQB#?x|di%%vHMrC5Y%!15a%CQ8W z6sC{D%J%K#N9@xsH~s!DJ%I)r{I?rV zmxz(>2Y+e1)*nsz^p`0|+j9#L3u%ggQH9adF&z=Nn}^r^IBDNIn}FWdZuqPO z=~=E#H76^@H}&{$4EYSiD-m(9Q-mTF<^r%F}ZSn=E0R z3`_JIbJ#Gm>x%Tm%V;snYI8Zrjo5Su^Iy({}09R&}njJK{ z*<+j5EN0^YI3k@b5pi+gBFClJ*yZJ=V(irP(jFF4R2+H#L%rBZi=3(K8vKRxr?Kp{ z6IP4|y*W=n!db&8<8I|~@iI~Q`E9^rYK5crj>dqhWx7(B6SHxv3N(MqKevsH$@ta)R6W~c^@UlzUn+Vqa|kg>MqRCQRj@x27K6h9{t*$qP!+575&Fm z_`0N>DoTc|dw90E);Hl1GFjvMwuDy~MX(M3=AS>Ii%CL6YhJI+g-orK7}dChV&oJ5R^cZDIqlQDw&v<~F!kjc=cdNheHX@p_QeY*_b6v5y(s+{+x>88m~(a~RVZ-?7Aa>NO{~L`X?~x}`sA?Vq{0XpW#) z$+qY==!C<3mQ+OxOn`hkyn%fj@CK`xCST_BC5qsi*sPUJQ!c1AG-ji74L8SnNyY)fHQ*U!UWS>X>bb z4~*+|FzZ+R-3m)@peD|TNN6W!p;GZQ%`D+P=jm5OmFIIeshNNxxvu#g={v*Fc4%d* z4_M6ARYjqll_wk&RuDkeV*ZWH0z1XeHN2z33Ck$i>*On2r|<8@C`}r@H>RII*{;^N z6i;gj_e-;MRDZ;Hv?p}m&#{hFe0X#J#h#0WY|asfjNIhEgnX{w5WFXSf%gEi_F`GpsOmxU66EUQX>oCgtH61u|8O58 z$2CwidFSB}683EihTu^v2}Rul6Qusm&gx{1r73mFHTvEW+t@q2J@n;KE(ZHz@7G}&TtxUh(z!BGTYbMajC2& zyys?#+sG3P;#jY3f&_+A(sx*UNg+=Z?4UH zzXtkU1eaL6f}VvI#Hz^MyYy6GQrYr=-FlqBCvK_RC~JEU$|9uanloU2a>Wh`lQQmA2?m6~9vt*bIR@ayL@4>5iv{+P}<){*DI92DZPyQ?= zGroM9zLRr$z|CUD+`XktxTF6_<0O4~+`T-kWcLC>H=KKiyVAc0q8(behCO=R)Guh; z^NugH5^Fr1TGJ?_qRMl&HfH9C5(v7ty>ph+0A|G5m~x$%R|W|Ksg910S}MX2azaBP z_UT*9SLIfp0m9`Cju$v&BBF5}@9H4&c5i1@z;aRVqzumTi#u6fd1l;Vc z4F7Cx@2DWVy7=NjLLE~q2-MyA1kbs5S0kB^Ht4#>@!1Y{bbr)svHmz)cd zNoF!tF@CU45!2JdCo6r6SrJFc=7~=-ef4RdQ1|EHFdVS%@a0H)SREz7^Zi*#zrGk& z5jPlvjDxe{yrF)WJ1c7&F<-Y{frvsZqF*Y1TmQLi4HX$fMEi?MdU#r;C}mf z?R>W{CJsR&Mn88_)Yq4{u{jMVaMo4U{<(I9d%d!deOw%&z#lE{x$0rnfb#8IUi}gd z_PL!G#KeGbo$erM;YUX^P9;WY6XzK0=)sE9$Kz>9>Jl>VG)aXgo~ok71Nh|=WCo2{ zoK(_yc)^Lvg@^T6Vlo?Nf~T_?aB*>{11!vhe6<8cUNs^nGOxPWvr8c?ZrcbXSs8*y)qeU;!ju~9^xMN6UALd7vj)u!M_ zyMv0TpeRA@yhVVeGV{>HNM3z;bj}~8CD29%1z?f8{#t*&*q2%)qc7H$w!KF#zmn3J zUUN&m4_o8_gTmsSI0N}S1I;{jTnN6`=rd7nV7Cx4zV8Tw1D=e?Ibn*eH|eP@Z8_V> zk&)7Qo1fn4D0x00%kuKFrL{Gn=hrn7yF>!ffAXBr|7v?XQZ3F-L4@{)z_ru|@W z2po?5!=QR!@UM?elw_+&?ow6=p|iM4&i~acbo(oN3C+7XVF^9vyrPMRmMs6`;E;h{ z8eJVsK>|C7RgU;Y)^Zo-YidbMBP4FNinBxZc6vO23I ziItuakiC;PSN#66zc+;LQTzv(HQ{l2jZM#0n8o&>-_QBW3O7qXx(ivepD?#EE?+ZE znMrxVTMY1;T zH}hRxAoi~94(6rFxIGma^CBM~cTMBg9)63JA*Ak?P&B2|JW~6zcfjemDl%IZmZVf4 z_Rv9}pWBb<29l6uf^pEf(%nA>>A`Uzcs@F6s(hl9p~;RYJsfMZ1QdPtMZ6jUa?y6K z2!RG5%T1(d+@G&YUe!T*ko?WgJ&5t-$BEqk7jWKyQieM!Xg8?`erna>E5ssTHXHBo z^zDzYs+IODSL5*G3+uYQl{+6l#)?W#GQupMA>PMbrd$a=BULy&C8;SiI@nT0@4yS#sjpV(@r| znqktJUR=y7<;<5!z`mBZ`@1KB@%lH~4CtJ?F~j&BVH1%EY<&3~PL`H6-;WKtzfOs5 zI{0b$=;(NL6$Z||>u7&d(>)e8wzVlA^<}mVg#ZYs__LiIUyLIQN8r>xY{){4kQ_$k zc5yLK(`Kw|-S@Y%sBSN~Hl93yoJAKH+T5&gywdV2ST8i8H3FovZM)2Mz+Q`E9RojC!K}tO|Eu&P{tq@>+CQ+;MibG?XQrN+fUpEgHm(6R)AJkAKRUVkK^n-LpF8oEwf%D z3m9L z!eSJTs@|GN$d<@FyxOxLX{T+KmjEDq8oBl~9=G_#MGaI@K}C@GE8oS)0?XH{T=@al zT)mIMkhc5F=%~l+O_`zZw7zVkz-+e-N;=5^bd-p0!Tmt2RRGox-RWso`u?z(>Kmg6 z*Y&EB%@npUatzTN7k1~{L9!ZrxShj;gW@JiyeCzoneC}jYs*%|4$-aa#^{8V&xQFjK_iEim5ZgZ$mE6&KEETe0VlX4-Pky8KAdnWta-{#j1_ z0hrP5EbjbRY&XJTh8M!?FLA$Ah=ySsAC^B_jUn-v+;pXx+d<@uswIq`?=t%X8jSSX zLN1zo{#+NIbG?Q8{D=!+0dwSZ)?lYcu5ZP8$XBx&_H(nR$|B?m0;r`fK6&!FVpD7> zdpJgitYz*g8Yhnph@Ya)HZ{4oq|dZ_FL_Fmo@I#g{IU{Z#LO-ZE^j>v*I)@squ@R| z0Wfy91WZnFKGS}9H4HoZ5>UV$m3`dwtF*sClXLHBpnN_uKZNU$jJ~suh`LCls0OVz zmIfDI9U3u{v!9Vx!lnWDZ)U*air2KHV6();4S8RQ3Enpc(>UVP6Z4Uj)g+dNc_#9g z89zLJ`P$*xOpM;RuazFJiG@odq=uluxc?#|(M zldt}HBq2>|w_I1HjG2Y=Gjh@*BoPHlFk)PhCh}ImAZ%ZEjKk-l-n0tDN6(>(krJto zZ!32Kn@#}`xInX7y5Lk2%p-ELJl0O%veOcAEZ%3e zM;1NZ=ss=T_tDmNmR8?vB)HAuEeAa!L|cuXGBtt@I)Zb;pd2}AM@9_w@y%xO)~jc( zqSA9BI05VJ)6cL9PoJ-~Be0LBCY_yD1TVHNbR*xj^By|Z?M+$WW!1g;E_DoURxl#Q zllr&k=xduSP%iL_L~H&It&H(?Rl;WAhTKpz`y%Qo7DUrE<(A+f6*Thg5TM)$F18V+ zvt`%4C0FQ1KR_%tW#f{&gDVF-}GX3GbX!<=WJ^oHtbGk2#> zY;Cl3a-iyCt&;M#NQBNOk$qq7GOY1ohfSODx#jA}K*BdkpVs+ieX#Hl)2MT_SMAei z-8%va8k}00TToxixz^AGd-iXsVT3JGKHcCIGUH}MJuX@+uZNi7qv5?vN-`_yUL}}H z9)}116K}k^5wisE@v*V#`zLGP%DnGc6DeUL3)9wCQPhPlu+_xJbq?evIKB%=JlV`Oac?r-F?#216Vr;j^jd}agYNImcVb*+(D3^z| zMV+8NA!_g;^oW5nLyHg|Hd>re)s7QNO1Ecou5r}(cstM^?FEc|&{1S95vbJnVtU+KhWPJ!Xn7Uj54Ui9L5B%vIDQxOqxOWXULPdCm# zj^=JB-Tn_id&4b5dLcQwvCGS@fC~Wp$t&$&WIMkb8m!PkinqP>UV4OKfN^)g(gM+e znXa$DfzOLrvq-m@Xiw(Xx&g!?j@IbbA^cPjoJOyoX$Td^Q!VOmkMY;HyHn zuF1j9@u+|P@|%s$aqd=L`kL&>FXsmqtYU>WkraV?&!;=&blDBf;(0+wkEc1N^h$Tl zVwM+=vySfxwAji-E<`lH+y^H1sa!W7>!>hUzUs@@Hf-c>UnHZZZ*LWseMc8KVW{0} zN|p+WRR>t?wgmC~Do}Lk5%iYBNK}?nEiAoZMeXzU+}RCre0g{qSRg*))?A{0CGPkpsTeT$;G1X7Wt{O+2;tl~}Pa z0g7MC51?%$NxO`t+g#FIFW(qIvAYsB&1(jSKriYavar8(`ArPl%Tpv7^S8Yj&za!% z?wAT%i7;&lQ<)F`STHRyg=%0|_5;OS1+$;$UknL}h&)Jx0C{K32V_mpjo3q&)vtQQ z#v7!73WwLKxb(NdVkMTDLN86^++6ETr{h_i?A&=7&CoD?8YbtgJ{dcE4YfcT{q0`I z;o*zpgzikE0tnB?9|-5nRw7>GZT?+C^?qlCdxv_&2W#1$&sy*-DS;dnK5#V!PNxo- zU#I=h%43vd>%bE97mK`h;(Sx332LFclJovbMW&e@%sZk--&OBkkK12S)&#Y&K0 z^Vm`rcfTr+3>^^mjhjWLND86OqMx^)&6j^tAH2`aYsZ8p^jmwW@NH0xy%<5(R*G*< zdR(g~+r*GPLXzdI%RcRy0d|&q^5ZQx@f9>*;^c{eXMOsH9(aElP}PozY)x9MIhtr= zb9A+SpZZT6K7IWFEGne+qI%;E z#nizBx?=PKbB>4xJzXG7>L)}b=TP0yloK2o9xRM2D|!c)bq_vHpIlM>qen)9JarHc z=Z5?vLp$fK9m)#K){kaBBGj(RDck8oy!=Tll<#U-d*qa*VjlMTj;iD%5b1g$=VZjJ z(|qU93oivB$zdr!dqSMRj*j&9xuVm`5X*zzRc`(f_9V>nmP8AA%_@5eZd$v|Iv#ho zjY)W2Mi!Tll;_w58q7JS6uRVL{nc@thhmR6Dpfl)41$K<1S8uGUbL9F_~UZH>w`7q_|>`)se? z)2~PI(i-NRidsLB+9D;HKYhl-OA=CAVahecx$w;AQ@dE2PibbUj2qsQ8~GF;FEH15 z`ZhCD{*nhr@8(j1m$%tlSsqqjU>2}r$Ul(&I`bE?NL2EP?`b2?yXL39fyuq#Y>RhV zy-|X?CqVCW$M-@<3-~931S1Kzl-kqq=+y?WVsS%8fxS)d&;$3}FEbxLqH*#5vHzVe z!%o-Hd#*V}dVZc&i6rIxTx9?fyux}4*QI3ezbryg0smL<)NaJ5mf{HcDfJ1%2tqk( zkJ3~9Mp~mTyY!DE77DjM{6mD~^aELF2=WcIQ(Ua6YN?DO(ns&+-ZZLGDGblE~ILJE8@ z^2-A2ZGjckuC797kqo~v`T(~)zaYw3rs7?nv`p#}dm zmvC;WDXaYs%`;v?jX_|3ZpfGK8)MZ0loW(sA~uE;SIWEL{{qLFaD;BhNYpsITAUtN zJU!wesQ^7Xb*9l}(t~RlInidQ<|L8K$STB-Jb;foV1ZJKFJigX zj=y++!M}HdhaJcKVJx$&P)|!gK22T_pJVIJp$_Wkz1B`o>aW7U z7%s_IU19k5*Yf)YmNkjmJnnJ*5o^6TgW zfJ-JjWyXNi`Ue@I*VL$l3K0j9@Q00|L4)r5LhMq}H-aNlz8PsPpF&*6?S=yPg)O(e zUuP|8UJ2ngPGxq-ER+PG6m$h+(z$Mf{5MCCsT7A(`7)otIz3C#xSz;gUp;1z{EJ5` zD?&Q_$;nK$bIiBgt!0eK){6llQ_xZ^$8D6UO;IFbB=U*Ajx6r-m!yPsaQxT52d zxT(|b5ttPX)nu&L>qVb*Z0>_RlPrazh8@==`~*ojmww~&uLj2mjYZJ}hKRb1HR$uX znU@)@)eJ9!w*OyzfP}^){_dggN8!(XE<`dRVQ|XdKRx#-VY%0r#LFS1`gmV$a?{i8 z&+^SSHHCMhRLk1gp{qga++650 z1Rj4>m1!u%m}dzLf%D8PS_jv!1187EXL)_%TTJs#mtjoX`;&y6GZDo{B&i)dA_?}F zdkY-s*E2sY)4VTWsbg?Qp)EHB9^hzUCtK!fDu?#B+|iOjBiq8*=qFw!%sWW}f%r z`rVDbU>NacK-f8)c2&Opd~fE*q^EZ!fiAT9)z8V=?^n3osRGf`J(p|Tuh~MxZcC%D z+lKU+ZFl%9ZH8n1=c?$11m?YuLxD zu5L$7yZ){8U2_qm*^x5r%_i_2Fcf6ck43q~*4tSUzgv z@NYqX15YlV1mZtcMf(2QgIMIO5F`|_XCW|XUR_SJxkjdZCb+K*xU5u@R?-W>~(nrEm$kyFh5+Sj-e| z)(A*DBw6BfX7wlaS$qafIkitRP=EGbmeYhqchdlnkj#6*Kw5yToxbwG>i3t##g38m z`vg`R*39j~&Hr0wZLJZ`wDD~uOJe^8svoe(?(Qzs|0zdf7*PJ7IVCcCv zD=Tw{{{+Z_#xp2k&=?z*fbnLb;9K_FBB;7}xszg)3U92kG^bvw0hHF^ry^RX2VGL3 zfeND!fZzVhzALAcx(iG7-t2N=LGkgsqlAq)e{@^jb59z-pO$WsOnCoFrp&H!7t^w2 zJMoZ`S7N?!#MgF?bS{*0s%|(i!(mP2jGY{C+mdBV7_H^_9LONE08(czsD8HeFgXZ{ zIiWT9s0@%7dX6Ord1@G#*8S9>XAv;lLwq>*yK+Lt(BPWpRIHq(FZI&vA)#H>-$5*}g~d37Kzj`m zB1(|Tc!tCYUVroZT#B#zzR3EumPVCAz`%e*!7(`xj?M98s7<=-`*a`Z`;AYh_~`>5 z-yUDy#l&GDUdl81TJ|;|0tKUKR~mqu2q7=aEnFST&#c{><&RcU-E78Si1~9)m!+|f zW;YmV<s~Pz@0Z;7=+BaVj~G1FB;UZY7JClqX#1GFv4+)(CqE(I!N=pq$9W2W ztja>Xh?-bPMU+=nTuod7C}7Z;9Cf*Vigiy&h9mTL>ms7{MX0U<9twtdgzRnIj3If7 zC*6a!3sq1>qZ2lqF)-;;p^f1)ShOHr?E8%Nk3f>d@e0km>DjO34Zh z9Co7m)EJmk^PZik-Zm@C3rHE`C>s>&k&-jyT6hjkqlu}nJ40BM}f~*stL-O z9ATb}y!sAUpWb$2cnuh{)@Xsa)Q0aA4JrjBQ59a;PEQ3EOy=^0GY;No)R~im7^!T^ zYI+mu+8ty_O|H2h$!Bg~WL;grL9Mwcp|g9GXxU^uET}k#I@+H!l;f(Aj^#7`9jG_Z zNNTo!Cv%X9!x9cqi+mh(cJ5CSC)7+yILWIc^lzE~{bhDb5z~A>I-Xo%x2!H)Ad6u` z5v|;H-4WXJ(evF&5JtOxs+m9Rm6p+GZQq2^ZQJSs_={Z@)Mt`-Lp*DJzyo#b8eNQ! zUWgGx`;uu=ta|d?l$8}6nkR1@J{M8xPHSnBvnt{@V4}1r2pvIMR%6Ehi-%9=fSse* za5p60Rkn4*$YVt3+m(Q|-_Y!K9buLE9eJ9udtSP_OfyKV15@Q|JPUui%6Ci%k%m`z z53M{=dXI=XftAaRh7hCLYYa={tXw^(6ct+#`mn6<^<6(S6x2SxvrHr;&aC=*M@wv7 zc`Vj>o|N-hS&Xj8Kec35Z56xubUNMx8LCayuWiNqF(hy)zdf86dI^$Ipx=18(VLe_Qci5`Irvf}*b zK4R?Nf&YnNu_k10P~TU(NP14l^aEXAV?%AgQHkAjEl}HW=?$#K;S(``I%Fawm08gz zZ*J>wPs+IFLh~W4aBfTAo28GJw2u#)O?9^hzlyn-UfO}XyGqv$ntyNfiE7DtwBm4m z;Zm(!!<1J?p)$qpb^1YhIMBq6Y}CxVXiqAjB{l-%n5HC-Cm0{RRil zkUIY9rocg%`gFY4x$E&Z)Vkl0W-Q@8dy=xHEb}6<-nN|rN}g8KUpkTD8&vObbxGZyX9~~aHS)S1SJzH_k04y|>Ap8{az%wVsWp&?zb=?1+wQz*| z&G@%zAs2$6k}9g+?paLg{&PDwfD7{MZZYXUE`XwVKjTpbOkxW}P(-&GyD>?iBd(i(mY!avKahHH?O80>O3&+yH zl)%cGt=$N$T-C*3?W~KwH7#5p?P8bmgFNXna^BUUdE1lk*KS3-pUCaO3;xYtdf(kW zob`DVcR6XGD)%=-AXqrj|4koRl;acr)E>-1{z2y6@E}32RsH%@8|r7MJS-Aau#wq! zq-R}4h=B{s3C z9||%}q#66hJ;UoCOye*src~AY-tjh^+6DynkKtEQ%5x|<%RH&xKg=&Ca&w!m!8iPg zm6Mgstn>xAk&WVzxWB-$Ee1r}q5~KQYzF97RL}X|ygdD7+&hntfx67Rtj_Cc?1kQ5 z1v+yOD@`_0ur^V)eI1yv4BQqQ55D@$3D#}=B*6`+$fYEHna(~mOhk68-aBS}^mgk_ z-LaXoTkRIKlX9670g~xg0NDSH1qjNgi;eFUZ_+UpW_IMf=xjxOSwdX$%?2Ic1w5vf z*A~B?Z#>p)yV1Gyl`W6_RKE3~sJuq()RUiJ{}=(}$9ed@QSFO8Wjog?r@n!`AQl2? zCKX@^Ln*nKgs23eu0*J5D=}XLSpvjz4YvZH#!?c7s*TAhD5$Wf?+ck|%c2$xnM(SO z(0F)MP7$~ZYfr~glBdkqhQb|)h}yB84RwX#S6(Sl+Wn!CGGf))xV zk0}QpHxOs{E=;+vXO%uICyLmwkxlIN(xm{pOHkuM)^NGT}7LqkLPRvw?95$r<|L{&Z^8!pJR3BEZs;(!uJ#@PJ2;W zM2W${^up!nCgG%36=k`xDXXeRgH5T%D_v*^^^9Ol7wVDj83jcp{NwQ+;VfFT*kJM4 zJO)Hf`$tccg(nu=7z|v27Gb^J&GD#q`312a+J&(H2V-9WRmak-OM;VN!97553+^t# z-95OwYk~x~;O_43?(Pm7*NwaLHaX|~_ul{hZ@o8*wPx+1r@N5@uc{Gpo7rLz zg0sGw8?AvYO_}Z-UgKB=*fdbNEo_sACJYu?3BxP!S#HhMr(fzjI`0>8>{m=$Z#Ve7 zt|)P(V~UgWm1&>b|7z(5sKT_~}rSB#ajl>DPM>_+LTV zBie<=QUz#`Hm0p=xJqI-TX0$CW@h&<1IX$c%O_N+iRX-G=jSJ&r?}xWC&oChw&9H1 z#SW(R=w7^3V=CaAb$z)iO^c5o-!fi~&s0sOXurav@Ky8>e1I&I(&=7NStF@CLT9bQ zPQaXMtznKPkk707J}3`ZSP>Ev!QYu=gf{9?>%0zulvIlv7{(!uvUNc;<|YzTZw}u<4-+zS3IVB6cspqJyYEB;3Ot(li>Ee-Rr3K>~7$@DDcQKO^X#)T+Hh2t z1}$gcuBe$=n2^-Rp7HFY54@<1-(PTvz*{DnwfENNF$yb8VMRefOC-oHS?gw!TdH=X zdB(~NR14`1$DE>+QX3)FhIb!TPvMlBe!6m~qjV2Bjw2aE)WE^3v~B2$;V#$rN7n68 zS=9}OU5UC#tM?rSg~gbGoLu`8oJ02%BmqVwp}pp03hq~2!cJ#_Zo%*yxwuV=KNV2= zHskXJ3$aqmVRiJ$*fHqjCP)#@VF>YgN|i&r`H)ISrBq8(L4Sy!mEgS}J(#%G9vR8j zg&f&k$_o7Np<89O1ltkD^{JJ%XDDoYWYR?>%&A-E3hTFf}Xa-e|a-K0KzMLQFceqp$DlINL|~Q!f}VfOv|?=c;#bA9m zvjY={k1$zyWBh%0h2XSbp5t%5AcUh})M3tlxIW(9Tzy@`2MKfNhfcZsM}lIL7F0ge z3=>qY7H*h8=JORh&S@^KnEN~W*IGE`X993q)@}|ay9GbYa?~pk@bEr2_0$Z^tVG-* zS1=&Ln6jLJ{WTOjzpGnjest3l6)t;f1R=*qU)n&^ zYBH3JSmLMkGBOqpOCE?j*B^SHxtRE95d;Fi4ecNM)<;;&fUPdATp_t7P;2t255Ncjl%pp?`JjcPBOmPi*f~Q)nY=kc^jVh`# z@;G(ZVs$rf8q3`BDp|h6|A0nTWJUWM=Wyw>>bt@rJ~Fd!>9J(lgbV1IwFnZy(TiJk&2mT-a7kjhAhDK zz_(;_k(XO|{*IfE&5p)V*S2t<39e{!kZTQUMJlqSr?0Q5Xo*=%Q%ei)G8#S1#CW$3 zJ(S1GZrk4lW-a#D1Bl|{n|tA>QH}C&gZ1s3h^!W!exE;*{;*#~Am{w>O`W@&%fy^1 z2?vAE)WuJ0#-p>^>MiRh7j@|M%~x(Y>CwiY^D4u^qA!+?#2kE%i;LCuNi`{-?W(KK zZ*RZJtslZOW{AqvAnC92K0l~f#Xs-rcu}E&ynK3+jogS=-AVr7sFbaaHIlM{`9OFo zw`gcDdeStlrO=Zb^)V;f8BAMNs#=Iifl%8|ACIsPxqyCf>Jqj+!m%>bDJt-hKKjB0tb}bf#!)-J8W8$;8yObKH8}_eJAJ89(-~@3|EGAbv$CV9LYMKR-&4t|S+ej}R{ z)PF+dsy(jR?{ z@Lv}~VK0`f3DcO;f-LTV%tERhw(rf1(F1rmq$=(2#K=1(7S2ojt=pP}lR2u-;vCPscQ$*gpQ!z~$J@(Zb7xyh;(0mj~lTSJsu=arwLK zC(-#wnp(#)SRP-dq}G=cDt&lB<KL_j+saBcSqaJ^X1PuQDX6-C6b}+i&gN+(v$v0%`1N>9gZryAt2r z*IAOHr}#?yTvnwrT~hzM%^{hMDo40WQkl;k*}tbB+sR7RWO_E^)uQ(UlxyW&4`9AA zB*{XL+C%YB#nCg%%dFRlq2}PO2YkwBLIusy+f<&gdEP(^`1mG%CCu5YURvUiPGtx3 z=-woE=iTOSf~Cx2+&?8NE`g21cpvgU`Jh-r8CiLR2uGLM6Uy`_74m4dEd}pwn?j)J zWUpY&l5uZcxrFL?jkU+B$#yB90Yb6c7Cj@?nIn6xTZR}7(`}|WL?7tq*=9bbZ2%OX z`}c6`MJ~GNSQ+=GtX}SHaXND^AuW?r&QQ)Ug-X+*(7HfQk-lv$HyfrwxWXCB z1S?z83tm#nh<_mhqDV?Sy%K7 zJ6O^H#lC@l4X-#r_rRTxvEfjhf)U&d1wIU3Ra`?(&eY68gkU4c*#H%V)UDFCpPJ7% ze@Z$Ll=|yd*AziPG^3sAGw92OKEzHY0lmthn&#$I-y+g=yPIlpxt)CG=H}c(2aXrq z-K?5l3&sg$+J6`RL??wrx3BGj{rOy1y|yT=^yu_V=`5ym(1fyo-H4LE<)mJZB4;|x z)s=5tbif^|0c#t*C+9JR2GWOk22h;^LQ#Z!Nj%Q;pHbsuxEB0op{g#fM^w}Jbj1+R zoUyc5xDpWkHNs(qMW%<&geQ;SG{1(A(N&rSs*Q zq+h;32+PT$f(icu#T2~RX7uuZ3pW_THwpH35iy=imHh30g!r)0=jVH!hoxAyJRY9h zXXkSnYQ=zKdvhKiCNrN7Cc ze$>{6fV@*sQlR>~Y0UPMdguohB_(a*%i;zW!w_A{7?lDrxy$%X@N@R%S&P;OD*%^B-8Wd8Y%P8Nsph zohE7woZiM9G+ZZ(9u`M;-drxMlZ#Y8@KZ#J_d14PUD}t$Ro1Tg{`%HH{bfcAbk}Dn z4`8w9^gysUT`xp&(!|0a`D#A#01VR7)!VY8`p%Iczc$o|qP?97lFK~}Ibnv+I+jEL z)4sg-4F!^m!!I~ARwt>e3N6Oft`aa9yd1)X<|T?heyy9@M#g_@eOOl@kWMAfYqW4T ze^1fQD;;EhH!LE|DF$z_szaTpcMGzEKFZg1khDC8xKufN3k)5@DNFWnUowo3C;f;I zK%dndiqIk0bhG~mbkEJj)K{zCQDb`~>4zJ&THeZdoan21q0`ZC$_B?#-!IwREzEvW zo%zWzQ&stZhhM_gi$Ahf6}B)Vb5EIFY$3i*1aQ;;L=%TZdQVR7h5|^EUgAhyAK5!A z(=>WgYu_pi`+l6xD?FM;*RTW?tU`X7t}(dT0LG=*q5REgNNmiAd2`>CsvIS;DPAjo zsM49FYv%ftk?|4HLxC%G)SEeW#+zHHu381_g(#>B@wG>4S*wmN^n{{(782WHW+yMw zgCoGmvg_-V({%W^2UnR?bJ-HdoK&8!M4!5{8^?M}MabS+e#0BXu^avmv}-yoG& zUvW?^?%k?B4+dAJ#ZP4x?%A&6pIC&YQY8(>`2=IA>#fyzr>^lUwnX-6#iTa`VIIKI3jLV?`4cVhZ2XPR1UT3Pbuig(%B$V~f1;pVenv>7K{>xUy|ijcOV^^%6?Mv)a~69Uur1JXOi0foM}qu1L9 z>FvRLm8&TwHt*c>N?##ED0y}+Q`lH6fQQU9%p-=wBHmRJf@&Q75nbKQZ1SKSIB&G* zPa=HB_$&R_XQWXn&el^n&<-Wn&3m$!L z#)YO!=LwGmrV)<8n4n>od31OFW~;M~AhaA;S+Q`Jd@f#s+5N&M%9}JAgJFz(t&c{6 zAX|fPlHz2s%1U!n7gq-8Th`*j_BJZj_@=qxd?aDs=ZN^Yk6}A0_glu_2>B&#;&y1x zI&s-$e6;csG_I^fg=IISEGzY=m5hx#c{i_R@O9cj7!qr4c&7@@Y|6V34n3vMkTUaL zV+0{aXLEJrW!?@yG|+E%C`uY6#h;~>N1FQ5DfcS4m?E+VM{ifQ${Rc7TeF?Gz(E;Z zVNmzjJg}6*T#Vc8_x^3VzP1hN-r>r&j%DWhwim;3w5z9jfo2MR^gbqJ`HAgBaJ6p| zQ2TZ*fOwX&hb<#$yRjZ%TnJZyKGpsjN|40INC;QXNN{Odg|?crci(%+?zf#r#@*dj z=La*U7_)H|NPfFdI?xj8lWi2ue+_lV)w}YAaF54CH9o&N-@tA*8@=$B>r25b` z%?4l-O_XXi?>Thh>|G|%I3zoI0sP|GWcPX*VhDb*@u>p53tda$AHK~c3A)Rh_NXXF zB~$0Bk=9JoE6ZV$)A#ZKM|GgDPWV2!5kbPvw&eVkb(fmto+X7Oo^AR9n-hSrF&AsD zIb4!ant#$7=xT+c;30p1mW+LL+Yr$fm7bR2CzQ23+-Q(#&3RluVt!~=$AcGCr}D#q zj$_!^SYidAQ%aOPN#A`T)uNx#4ehj~+5TvShUypt6`>6vHZ`pQ3fe@?9}2eJ&k_wZ z{qn3yRa6@&K>@F*f!S2z5P82Pg>J1rx)cY?q;p5S;|#={6RCVyZP#sd+mpBcI%oTS z0>U3@LRHMM9bCV#EbllG7Q!q8T$ZcG!6#Rp3GxMeeDB z#C`W-b-NS;!gCyVtV*nV)YXKvA4wZ0l_9z%(g&Q$L7jyI@FRdiF^W+Y_!{`q^7ossE5y^9^AzReVY37t0xvM~}!$eU(&a$cqq1p1VYaMep^l)SDfv zWd5N-a!P3ohbD7(Jw?Ask#bJwbRKlFh7)fuGm!O-IgX2f>#KP?Q-?csh!Mp$h>`9W zgu2EiN%qm>Wx=X@7u&@WH!_Xm>ZNI_N?+wPWkOThxLm?)UI#nOr~2u=JEFlH20`@QzGSzmSW3>$Jlr zkg$Vg^l8J&+AIYun+7K(V2?a(@p)Mqo7xI#p%LN{QlYPykjjlL$?oucw8% zJ|jfXIN9KGZ~;+u%}!iCH>;eH?U^G6)5$@1<|i8}1miYYg4hLR_6YY>feK|Jw@2x( z=LC0quNOK2PwqJ}&@rr_8F3*>9F!_HT1?rwgFv^-?BOXXJ=zggq)G2|IMrlW>U3A< zLarrAsk`L!`c;sCWuiifrBLpP3$CqwkFM)vdDw4Vo(72Vl}MsVY;9 zf-1-qEf~OP_%g9f@ryi9XrF?-HuFQOc!_*(lrmb&i8fF5Fbbjund7|*o8IN#c%1+D z1`9GSKtxlhKob5vnrbKOcl7VV0rzXr3{Ac~DAhXN5%=dWIfg#*wf-#4aOx=Uys$@R z=XhFqZiX&B>@7bmYo|%vr&~>IV=wYpX*SoVAOdg-kSYxBk@{(q3Zv3c<5VZVjeZWN zYyl(`<%sMXj6Mgljd#($4quoBSz`m1I4A%*(kFqNeSSWY|g^ zIJsmGAeT_jm+av^l`yOUM8xPCMh7G~l;g8uMW zo4zE`Pmyd&v}GV&o>AmGB7wzq=|QwXLps;;70()%ErkW!UdEoS_4O+_nPWV{CMY$7 zN0OFc{&za*{C)q?FEIo}jgQ)bC`~@MI@9)dz9=y~Q{+uyHP(n38nwZA-fg>MsLsZ( zJ&Pxa>`-3)X7>jR@J4O56?<&C`0PHf62SXv|Fj>$wJfRK0oL^U5eVRQ`SS_-kfn3N z`eXEM5Y$O=U%08`swAUY$o1 zf9VmtD`T3OM?uA?43- zwKc{!k*E#^b>tRN=$i{#ZR6BUu>`mn9*poF>F%oZ3>nok0AxQpypMDTZ8;6h66Z37 zw~C3~!dY=tGy>-y&t`bdTwjNk&BBxa0&3k9IIS-3Pes}+RDp$#n(SxDDmh91m}ug+ z7**CHn9E(I++XXj-}%gOTE^Af{w7iBZ#b|`0IwG;>K+j?1byC{ z0x{jnP_-1Oyq}syyxgol(NW7KF!l-?WCY6ex5@yAL^$7(~fPN(|k~r@qk5{0mX~2S+-$8MOHebU7Dw zNFeaWUs~33aSO9Zo>PWSOQQwFcOQ817lKf?zknHmf9|6fU`PS&&d`TUCnNJ7{OOD( zal#}gT?ppfpknc;rnX*T{%qff{P&*@L`?({*3{I=DJcXzd|u0^0)bzhp@p^vduS^v z*uh!rJ3C@Fx?o;yJfK3{zgr|&L5!wQ;xsqzQJT_EpxqhLFpG-xDjnq2w1)lGGrra7 zyy5$LxN@k%?@b7MPzDbivS!WRP|Y`7O|Rxpqo837ME(u^0aO)O#+`qL+5^Y_L#uxW zAPCsyH512C_=V7KpY)GQqRD4}f}%hPlCAwY-4kQKMOGkR6cUzDi0%*0v?=`=j~m7E zO0p1CI|z;sw3WsgerWdyI|xUxFv+YAPRWV~0mnlQIDA6=A^h;3bD)1n zA%yDP0L3j~ewXn`t)QhY7nKT~UR63m*ckY?StSI5Hu2n2=eRDl=NFb;U@03|N^HvW zm3dkH4UNC)14o_Y@J`679X;>R7IO;66bM`@XF)z?(pPw=shD?U=xuexJzEX}0_svb zQy#m;hTQ5D&eHDTtR=gzlImh5z=TWC}UEg)>;pn?L8N4beB0 z=KHp?q;uwQ%uoAJW}sZbYrE_mshHG-p#t_K>SFq#xZC4=PGf-m8ou9dNp|v6Ld9E# z%4e3L{{ImvF$GRP79@OHs3YyU*f)e@3DTfOig@?~}w(DUn2Z>BEN?TB*>ExD7)=9w50 z>FeiXNy+h*Z}`}ImHO@*7B`#zH< za1@x=Tpv@+W7D(IsQzN}SQ;LbR1z@s~UfSwF(O?=mzFR?SPv@!C zjAl^De2rIyyP>tn;24C&|(LOJ``50gi&3 zVn@2JOdsXbTC~>;+g-F5nIe7>L*G?a{k)>))G+>KSJw0iHoMpN>cJdGEHIsZziOn! z;>P>>&1Dz}E&4Bd!ibbZUj4=H2)T4D+SY_{6wYs&Cb99$kXd_}L8;6!ZysPev(5FimO+j0_=t*&`!QG1jdnKVgk)eBaHQWO zX~nsDiO>hhd3ihaIG(QkzNMvSuyLFxZ)TT&yQtokqA)PR%7?{*WxD$Ie4~9{Poddu zk%=(VB>echa~0nBzEVrI6_&6!8$XtB$tmse;{bTmUl;(s&K94~(S1q!*>i(Qz*2&- ztGm?upI+)kboiTmjnz7HwfmB=7I&`H!2uz8J`%XR$zp;s)=fS-3qrU`rZvU7DRDv` z)22KND#0i;`08YovTiS6&ErdQXgcWDYePO~OyR6P|4$f%$3deFWnv7g#qxzI#n%9j zDT0C!eAstBr|j9a|9rFW3y1VES+4s>NeM&dCF8EI6tOjfoL)WP4tE1`dZfboOxTYe zRxHWv9^3l`1KDq`T<9dxGa6!SOW4@lg7oAWA0kL|7*bPi%BT!9q|wpnzI@6(h0|WKWbNToyM%SOgG{^oN5b=nQNcX z-Cvo}l$7Kg_(wbqKj6gAR8r-Zx#K}**T1t5!9x3rkt5IYDh$w!?~64rG*EH;(HF1} zIjo9lDd1Re2jwDHermREX3Xv%t7TbJ*@kRxh)j|ShBw2yknnwCwU!1k-!dyq@4DH$ z(+*9Rv*CI)KgIIUvjl2jI^Fi)OWo_lhm%Y95y5)c-$^|LsI(fuLZ`>3g7CrZ6BrbiWJsD`|VQKp%o`@m-1 z#u*v+OUJQm^_A@M>TsKz&K@(|*u{q(^&hIX6Qk{)5z0FJQ9- z&hgdba5rY%l+8W>e4l;}VRj7L%8;dkh!$dWmNdn4>PTF)mNha;Y4ou0ok;;ut$)WM z^LoDtTvmgDy{0)bvH_HOu{yff6`FnGhKVCHB0 z)4|i$XfwwNZv`fpC~PM2U$UvX-mF0M?uU_Zr)M;Z_2l8_S5$LKl|rk>qvS(OY!VI( zjgc}^H5;pbGg4+zTU^Y}e-%fFLfTRzb2T;e>*MVmITN3{w7UJgSlRp$Gk_j1R7ky#EI+&UJs zFIy40HQv+V2@mMI{fA$ICn(heV82@vyhc*11<6mh2k1@DW}xWSa7`g0lj8i=q2mgw zFI`*Ha%Ta-biTi=hT6P&OL@p{pR$MV)8u<>8M-S6`FL-C;`~c`p@1!Orgf8&{Mp4> zQnG;XCwb14IP=3t;t*n)H0Y*AUu3md59jt(Et2G;{49xR0IbzurN1B85Ts0 zvbz&i8NW2a?z2sP^I(oGhD=kqeP@K-2@pN;Du6SFWVJR?ZNJpoMNQcXxjd;?(XhIB zrtqF?Jx3Tuh3u8@D%{3lD)x2y2uOdq=kQ7u?0;6RKj%iLlvbP-m^-Iq2k!Q|nrWas zxR#%fnW2b8pj+X+gxj$qsufE4=6mMd0WLFK`m6pn+QOpN>a^n9TNz`uT+GuA*>{}t zD$Px171j=6JHiBB=@$^txVq6R4;EEJX*s3g9Z*5(y^F)vt1I>zYF+xPy`H(=*V1aT znT~M|*Uu+Cgu+$Epib5>g~M19)=myDwwZlWPsUR^{`@@RN{O>PxK{BSFL}9Q3A>Qe zywBP1nmMKJs181UmDw?gkq$oT+c^KwbZ5=UF|K0jYuc~|OZ9gzcpXi1VmCX?TL1iq z8@~_dV5|6ZwvVTiLP23+N=miBz2c@Oj$JYuY_*Lg^{)?igL3GbgdQr`hd@|3gtl?+ zFwBI?@0>x9I%i6KvX{CMr*$QFBfDWV$ePbrF!##r{UP7H$C~D^PIi{uo`(zt z1O#Mbmb;7p%Y+&_u3CEPo)8!t z)W^)b+Y*CLl`+v^fgm>^NzdeaZ*uMj(q04UPgq|sG+5`?k$$tZcK>A=!P}pxo_}eU z+aEu4W%k_t3AmnDq1UK=iYU)p)2GH1m@)_UYjG-$8m@?XRxO7#UZK}-n-jlTL!q2z zcq#}UJ+WlD4S1~gPtJA=r{C04ZO>Sd#W`c@sQobcjQ-xO;(yXz=-&quA*#X=rg5o< zmE3qYYF8Kolg>d(g6G*VIS6!9$)SpK84?8GZSwexhbTllR zRtGd9^nH+H+QW3Or|o;z-uH~?(i1D!R0Ld>l$}Fc+{au1uB+AdP3~k?fQE2Q3OvYe znW<78$!L0;7%sN3!f*GP69R+&24!=A9f&RDP9L;$2;MR4m#s0u_UGa^1-n7iUt;Mu zXmmx~=;#DUf63`+a2L7d7)pXxl+gdEsoK&T_KF)pKBClnLW|{6c-Yo`O!reN(fah0 z46dw?DV#(nQ=B!P2IqN@7T)jxmlqE4bkpRTx~>gb%(0fWa-$8imL>=PG2Fh_=qtSD ztLQt3kD{C?(~P%i0mPt(C7J!-D)X!owC}DGmMXN$(_)3?UygDhtto+^)P zC@uT6dwKDRY>($LK|UEesu-}Fn?uCY1iMtRC^-)34|vrMk%H`nNQ#tY>c6SrTnsn9 zq6Dbbhi37nWQ6k1Imh34ZfA~<3Uq*SDVeVy<~I+RK&K~`pWQ6EDjv6rd^ow}$Uu;^ z4@l2Tb~{e^pIRKP6upXkQUN^(_yEzUhYoB&%SZUC)#-gnuBpS7hy;jg!1ix?(?Pv@hfRiLp|4OMub1hy%S{iA?sOIGd%MQVexffZ1 zx%6;^J=_y+E+DQ4*aO5wcF>#Z_YDC${JX;sGp4`Qi=fi^y=xY*Rmsha_<=6X-`~%QyXIEFAY%AC(+zf5=w z65>Yll&p@7tj*azn_9v@;GoZMIUc+f)ghoSi@q5y30mxhKZ-yogy>*#=O3$RF$)i@_#Xu8o9Y-qtZYeize@f>+*-EN6@0P9DtP;o|z<1@X)`DvRq?VDv5l?y zRqJrempQNd_~FlLMzeAg|71~A_Nb$-r~TA^H(Td=x57hlH?lX{u077Q3L`@bqI$eu z&I(;~N{|DpUAJ15gm`nR13q1$p0+(Qp~OJ9(!qC5=Y1;ST09npRqsjG_zlBEQgx8J z2Zg)#Ee+8y^kkSRTE&@~OrG(=2@{nyF@vlH4~||PEx(wFv-y_;xjMLg*YR6#v}0rH zQ0Pqs=Ht~EqZ&-ej=gLpS)6F*g@usim4p(XJC9^6LA0O_VMP&+IY+7WFH&qi+CXnlIFu1fH*}X^7hi;DPbB^zWlPwSTt*DoRdSqClJu70%QXal~ z&Wb2YUiIYwzr6MvNbl8WXOZYW*lDYx`Xi&oH~NK;DGXTJ{-lqZ*M`k61BeQVg0(GS zmuXUyxHu7g_ab!|EyR6!fm3p#d6|y8?CSb@^`n?Q1hh?8ih|{jN3NZ0J?1G8IT58Qe+w(6T zHE?FDQna><+Px%gJUYRG()t3SX=PBw!MXE=D<^-f_PF9aKZbNyAspJR z;4pp)c78XhuM&B}nZs5_@_wnmH7t=Teq4`ZH#{8Bg8Zn)yHqW$PWVdh<&*DCx{VmQ z-3bU@+-X?QP!*iPj`8|KaH99wYSUxHaS|R zE-w_YX+0j?rBmAd$i>)M$kJT57H_1VOOVb?iZgr#~IFm|=? zmGpt5om}TkW*+{y@^lel;QZtEDt_VNiP{XrV*ii<3oGbuMW>_K0Hgi{WnEffdv@uG zTkpoWH(N@b;NaU`3(M6$G3XEOPEqfQ)aLP_csK6sen+IH^>X8sr@gPiRnnZNzaN^) z<}IWMVr|hkKN%q@2v%~GbAZ5k7PQ4NT{Wo*Q4-_h-BAvnXAfw3Rg)Y{tBzr`aqqPT zL|#u!|ME0ONXl<*q;I*aiJIZ{6c<@Oc3Kdz=whmB7bhL3*rOv9!9j}!7~2bjoFswg z!`l3E*VuZ*9+Q$R@+tBWwV9Didz*n*rG!JWWBzU!O;~?xV2&at!-H9y{z6$-TKw5& zG}}vIOKI!bz$4_5%`irKLyw^}y$_V&Ya9Qj{k@@PUkZp)k66%bWaE=%JfAw&DjdHf zLn!7%FK51&KD_);5aDcZ2aNv~5r~=P0j_r67hZOB$h^nrdt)*F)u-7-0)f$X#`D+mMrs-t8vikStVc9wVD7=(vu>?5~;_LK; ziE(f|Ld1j#k;i+NUE^|RJe*Xh2s>-0n(M)SSwY{?-bBR_?a^HfhDKYe=$52#!YN1# zk`?jt)7f9pRQ4pjwOX$AN;?WiZb@GW{Oy)Ij!)GQ>*;c=F1E3-`DK;R*)30&GeI6J z$VqKDK&!MQJ_srC$L!~zpfNQ%*lyf^8g9@&7H~w3UTaA(wdaB@8p*tHikSb59OA~g z{)UC&tQBImR_5%XFtG+L%xs}LAyl~sfcNV7Y1;OA*7LcK54RZ!`gyy9fH@N z-Y;~ojsxwl-Zv*y9N~{0m(TT0KdqSTpkR=|-hKMqf!jkJ^hqoTF2-QEj#{Itq|#s! ze{C_*bHR0?dhaOF{r0GvXQlOaY>bVZt;Eu`4_geEUIMj{kDhNN@pF_taXu7>;wtnH zB)%*hcocwW~@7k8&uo*%U3CzKuDuqc5SXJb3Hh&2g*Z-bQKQ(;p zRpUB$fwe_($@)>A8D3XH1gGhnpVZ^?L~ng~$vihruh*LM2yRKHD#L9|!1KUNg^4#<_Yv0{b%w(y@dw`Ln40;R4hs7}G{o2wVkb;2y0hm;&^C zU6Sb?GCR8mm)0ZdreaN^usSRXvzF9_1zh3VGIdwgw8Qs60Z~N{P~F;l6i!Dn2FuRg zJy6{pXNr~H^I{w&6W;RByzl`|Hb?h+E^fjo);e&EOZ92G&Ii$4*B23s`v=*rl@Vj~ zRl&1%DXGMB=A|Z?JR_W?B{wJ*Yw;a*#yUGZ#6JvO8yZe^*koN(5P4R+>M*qRiP4Rw zzXJeX{t9H4T-Q@n)U7uZ)~CrX-glJaa_9T@)CH?!WtqI^Se5rBt~Ln9RFo#|zSh(! zDk+k)K^$F!oyzmTvaag1Y_+_{& z5|3fmXPR3JjrSH0WA&;FC!Nc{Ohc0=yhY&M739d21!q zq<-Dv;XzO3zNxmyg#R#Lv=Q-E7URzC`Ky!0+Fo_i^bvPe)VKEQPeAyu$r7&4<4R+x ztr4Ybo_}H!aGd;a#YLaNvm>st=E~*TP-aO{CVjRdcz?{wV0CxA z-)KcLYK88Bgi(d-ZxYqKLW9`J#;okltzQP#R*{W-AWoqNx6CeVU%s z64;*8^Rf!7*%0@D$Xt>&XhGxR&UcNCv7=tEvcWbp(N0Ptl3Aq`gmH+*zaHq!0%7Y|vNhod82C6dKQCdXPMK%==?uE>nO-V7bmU_R zA73gN5z!|otswNopOV8p45dMq2K=gw?3)-D+jU$;&L_JT#*v<1sJDY2k3A}ZAMxfO z;-ogNfBL0R7nJ5sH>Da%_Ir`4(LGYn@is@L@bP+CtXt+D-O-OREm17h_^a)>-{*8O zGWC&g-Pl020)!Yep+pQ&X}cxT=OL=Yi8;*&Jl)=%XgyL~dMLdt8KY0M8S}k5Gv2t_&)da8{n*Ck=jlW|J&qR3kiwY?F8L+ZC#BvOt3IQbo-q0#jnWw=swYtxS_#!@Co zC>fH$%lWD{HrSSDhkAG<{{Ayl(z_}-f0X!kAIFfYz)BodOXI~`HIkbav=WUNF;v~_ zdHdY~u_8A~8?>tXOvR<8Y*CqDlDUK2n;v7_(z=9J3rI-VN&{=xn9aQEBi>obs}!^n ztw@nCx{k5-NBbn|#H8{f;NA4&N&~yldWBZfMjI=b*x^UpCU`xgu<|NL{Bi8ofN?_{ ziFrE0W!Qy}9CV(2`6|ujr)(z6eE{nTCMj@N(2)dolT29 zk?8GqM0A74Za$OU#e^63yTG3$sA=JqrIK$ z3Hk?t)uHx`{#m?`R)(|FNU^r?8q%;l(-hSk%T5pYm@30}x{eVHs7;4`kD_>% z)aCat7O%PV^!u$S8tq3%E|F_Nx1-Pcp-YuoNHH^cW=WjgRPgf*S%N+IJZs5P}~YQIEV(C%Qnt-3W_e@r%NsA(v6 zr`v1k&2aTpau4!`1wS{&>;a~T)hJba#k>mj^D=UBOkq1l8wJHZvhweMS|3aM;ouym zrYDoQ9DX5HT1*YT=ScI94_%`xh&RKIPX4Gb^O7UIv(BZc*anoK;I^q7T!)ZcV2dG- zc3NLQNtP6DA5KeqH6=S?u&#D?nj?}l{5pFuz!J`AX{x;qp)3~drRgiRS;*(3hBqZw zvpPAArW{dabUPDqc10QjSqke}Lsu(=l#{A$muC2SEMwY$kWt{IphM@v{`(CFum`z3 zqk%-6$pbob%B@}aZ7+@iOsnSOIq7XwS@!R3m^8gv-HZ)VwP!z1|+;+tZBCfWT`DX5+YsO2OBtk5rhiF~!*| z1AXT`BBKNg4X1j)sOZfZE%lsqo5*FBWyMEKeI3k7yL@Ym_qocA?pGgi@R)n5paCSjfMV*nSEGxi?FR$5mX7iw(&1P#e zdS={zQOK&R*P6!`UEq9Sc0$yBUlSQ8@K=fe@@zvwibMj-F}cXpG({Cz&b0wIspx8M z&M~%x!qmE~LLX7KHbm-roPED+)y|w6kT6Pp=av9%%)Z;gaU^_4*b#|}Tz1fA+{*TZ zl`)nGxA!*RHTWXBsXPvay-CSg`F6ZP(ZV0SaPj(!i3lONK}BJ4^o>jz{xSqSc4iC* z6z-u$D_*=Lyk@O}a#>*MQzI%zt?jVM>&oNkeAU2!d8hQppfbA~QThW;flmEyA7AlE z3R)Dw+3O}@6cqsKjL_X%@V-KI`vr24w1MjND(5x{wJQ9#IIrPBU`Xs261O5@h2W+@ zb*FAGLG#4kzPO}053dUTZ>ju2G={Cz^AeM8rdFs+~EbC3pK+&iUvzMU0Y6 z2n&riCE+|#BTwr6`s=xS9uC>T1HqGDyu8|s(3S^JoSM}tzhC+z?;ae#@82&kUwMA^GLm2M-Gejzr?U`mCWra#e#KH0j$-6wp6ej*g4<+*&qX zUtC*vxqxaa2F(MAq^%-|=Vd|}NVM~teG=DIa=wA@c&~5KTl(PP(ky?(`1sg&}4BLYa zR%624O}oy3A3>k$rlrB+CdNN_z`;{>vgxGa@$)DT1_Z#4;e9rNd~^LWAZA1 zbOI5Egk&=}1K$PWbqWW$Nj~3IAc(zlJ;VEw*YcQJka*hK>-O$5y_?9{}9dh6Iy~x`p{?kgW)J+Y&>~`ptrG< z#-1j!iQkMj$&)Hi6Ya$3>DihX&jUf2S?#`NwzxyKi#EfZ#~*yUY)wb9$SNd#hMzz5 zgr(gOgxrt%AHe0>JJ9#V!;r->VMW8r`lGAs6YAp4mPVeKhzLA`eQ;p71NjBJDD+L}jX`U;22mE8 zsoD3p98@vS#Re^)o6D5;r1Qtb_z)v?oW=J`*7D&x12K!|1uKmy)oCBEqAPyV9M^+; z%h()Laa1`s<|PLPe=Ml;mhP!~2YcclWGqX{XHp1pp4KTkJrJ4@AK_Xqbd{X9dMK_D z5szY%yL0<#TT>D-!c(y#C#>a;n*UJik51#*6l{gLE_;l1ZTsMT#D-!Ds?Gp-`cMUA zZvoVPk_9cpd60ce7XTI(F~W6Ux(3&rKOSm{Do?On>lenfqqC3=;ST1??eLl$G>YYdJ2&eR`< zz%+~AiA?#|OL>Yl>`{4l>b&ri6_5f2MJxHk6@{dJ@ozKIb!8eq-0#1Xr4?qoSh5sO z)0iP~52|VUyY3kXHZP$O_QYH0$z0IZQ8;5nmU`khzKKdQ-7W(SJhT~jG_=OAz|3uc z)$UKbORi3~ZPJ`iT`n&5Mw_GFDY^5^W4~%{L-T#zpEf^~)OuoX%<72$r``i7YxH#0 zP=j{NnnOmD4TI6Crbb3kKHEa4*P4F$JHsrOmf7WlRC;^-Wv|rgQGTF1}OP+ACLBeWCv1Gp#e4L`74$&+^6!Hp)7DbGK}=CP~-= zDN2A;Y!4Vy!;3QiAWT0!GDx3FBGMh5Mq29E@bHXB#ln|6zViXvMn6>Ul1O&U>e^iQ7eCK^s<=X>NOyBSiA1W4}aCFdVXo!Nwq7Ty2^_@PG=p^*~mf0cWT~U zFTXsqyOe#KMd@HiVZVE96TbSYnpI>(jh!m?#&`pXCK_+qo-H<^+~}Af1bBAg%;FzR zquXc052W%lS~(OQ7Td$~uj0$JC8t-|YF>5~Go^p-Py5ph3E*T~Pfs%CQTWvvg}+8QRzRcLq)^i;*C> zb?LbZ`cOIwFJafH5WQ->-eCj4e*u;Ak+ws~U&`mYG6CNev@)|^Ck#ai=JUdQ5lgYQ z0yB@w9Rw{|SFCYThKvGex;M7hJ9Hc4OoraiH~gw?mAXh>lmNT5v@RdqsgvNrxGpzn)HNK&eFYV!~EsFyEx3ySmEtasny z=+8?&Ue1~_s*mIx3^h9BrtT|aU=zVWbJauXGipK(DqN7!_P+U z)~L~D^q@QTunI!c@~s||9W#pWP%*D>Z~K-%RkJVh!|08OH79tB5FAExgN41jwV$vv z;O&6-jstNQwYaS_itnUmR!tNdFE&?D#$%F8N!LF#{vS~zY~SCgvFxC5a}4N)U5)%+ ztu^k~!sr}`%_*+$!9%w`f^ixO7b7f^IwQ37*-(eK?L97}-a$Su+li=kHhpiX>xG?PVleb#Y8t1Fj-vrc+O^o{1f6*>Q5` z`4JW8Zrf00nEhN+-cPpWZ50vhlD0RI&8*9>-&58{liSj;+JC+1@&5F-KCuN(kvQ`7 znD68a*bM#fQJjj2VB3S&U)9u(#Uyvr@2r}=vznn_59&%6>Z$tTZo?hiAtSN;E;7BK zHz_@Qd0E)p4vHf?CE%Ov&Ls~_yN(b&;9b%xd&E&^M&sf?ehOwwF0V~0t2r=&b>j1l zr(XEFqnOAXdw%$(A$3Ec{w%0KrNC&l9||~mf7H+OxGCh!U}#-gPH1&*&B(?xWyoDz zT7{3AOs~KA&Nz(<3?e6M-fPhu61#_sAC4s4(ViKDR>py&-*n@YY#bsATMp<{zCRw` ze~46JV}6jqP$HmHv(phoN?^3xoKT+;y21rOJHwm~6KoN2?8p+-Gk)(>H`Qu4Ae^N-k0F~I=0n&vX@UawX{+O@UXoT zoDn$31ynFE0;`|K@X+J>DvmoXhq+`c5DGp#CdNc;{GLcJ?mWQzu9g{4Y=m$?;rYw; zGXJ(j51+)^0zB}zQ~`lZU>bqy;7g; zx$b8yA(VFc%;#V*gZZw!SN-Bhs8TV_H(?!TnsHmCeW_?Om(rTLnxDUtd5_&>@Wb0) zn5$B&sj2CCYOLAdvoaJF6$RUHbEs0j`HMt4)?aI@k95esAT%>G6Tx@|!we1YhSZ@U z5xK27WNf;D91-29iO>N!>kf@mu7AS019~*kK*f9|9PD><^X~MSH5*weQAn?g#{%L-4l$GFD|dfXNygILV|;35l2Gs zp+}x0(Eqk;%rTXRBE8I=87RbBQg6ckLxVlTP3ZRTcK-uX?Vs7$nS^{M2p;AiXC#Px z#kahym6*qKd>3z|r{|;B4P70w_Al@$Fo31kzvsYgy<%sf=dVA>URY3&*;a{U5-anc ztlHl3adxIIt*@|;@N3K&PFGh~=)^>YU?^4BmD#=+IxL)Lrk}jM${xY&Ej!u69KIT` z0CwFMg3v%9LJz6!_bC}*XKyfDx<V|<;^yb)R2B}-V#DZ?%(bU~m zGLvEpWh?rueRU!RU$Z6S?8hbh5Umr<+|d@kp|lyVrk9Tlf8OsTab|a8mdyu37QoGl zeYF{sMy8=k@(&hoyZQa=0#7C>4Ohl+5oyK!n)#B&+~rn7-l_ePHiDWkr1*Bte*DmV z<2722w8O8bv1$uO8+}pk%-i=To;Gp6BT`gTlLwXvv<;OmcUn9v0vleYxY}8A%A*d~ zRUa-ZNw;3R@vz_qHp+=?vZi};M$1emmFOf3-vYVjCL6e0oWvMc_fGP`w@lYxj@0|E z(*b!|w1G?EFR(S?V~@O}1QMDn`z&V7)cvl^8@3`1XnsPTh%4Lxw+^$gj82J! zQk{40v56%2h3wHgZG0(xeX8fx>mhZ{cE%@&9_sUmIJ7&u?;z2R8VyO7B*?*0%oW5K zXYC+91yQM=cYQCc*(&YlYmQ*I#o?X&W+tv(ufGvUeOv&ddB)~#FnL79shP~XpT5GA3FUd}Khi|@iLUrTGlOt*fzh6|K+<_Q!UVo4 zLSEsWV#TF*r$up2`Kp^y>wlX789sQL3!jCfq8@gdUKtbH;}IphL2Czb2pg_YJ-7fA zC(1nJt2vySuWAG4FFyjvuv%i2Pp3o}+a4ZCQcrvv0!B~IV*qA1leWBm@%-XKz4YDj z@gbHcCoc907$(QYUNx5RcTo5XUyvxPF=KE`-e?kAM)dF<^{*E?e0a5ifleFZ<13??Ty!2VW?W1UXqRK zZbcSY8!9uJUG6k2vE2ZU0R)Ba4p=;qpWpBfK<@V6Ta#Mc3m+MksbR#BS^K{l&k{4< z`Kj(5iIV205-2y>!y@nLS3B={gvvojzJa(1RCIelO(1sbwz@ZrBzbP~umW~}h7Yb# z?}FDzR?-59UWE@hecacpH=x*!tP{#Kpc~H3UH>ZF}T}t?P|?YEW6`k66CG*O9jL{-7tFIJR?B=2vc`doEky{Br9c!5@9NZ=#xS+&%Xc%et^JE+Yqg%N{SmubmGV7lXEK5k#e{ZU!J_&x2E7H9LA(*nK*mA#zH32|Ib z%%-j$adaxjKFX}aBQCrqZL+Tgq-G0y7*kuZx-Z;LnU}iv*XTJ|rM}ao`tbQUF zJvl4fhddUpTRZ%wu!&RN(1z0P-Eh}^na%DLTwU9ZKzxns>D9&B73iBeFC*#J{$z^F zS)&y~izWM%111J$!N!yni9+iIvP`+>(Bi#(g{KbOV!CJCeK<_~*4lcT_nPol@D%Pg zC*ty?AaUm+N1Xc}e{bYth{gSJ)SNCMVfu%w6JDtx4*4h0oI+5?zEywagT{lY4C**m zTI$LzY%OdBwIZ;M^{#xj^oX0nqbb0WsijYxemJkYrB%SiQ-mxGxP<7cXHV`_jse); zTJjaSaJULmik~L$v!$ZM2D%$Rb46-?)(wA)js-q#$MV@5*i$i-lc(xtmS%6>aX5C} zSE<1wBO`a+)pZ@nPHwLEhKY)cnO7WNas?(Dc{~M~c|ZUdg{;#Ql&saV+pU+Pdn^+Z z6U^ZUlZ&WOek3Vm9S7Ug%8D8~^FK!f73QnYpfOe=p3)AI z@(C^EW^^C%uQ(rWmh9l@I5|CC3dF?1NPU9_Z2 z-r1QT&n&VT`a)F+p?a6aodxvD(7f$1>_q2mdC7)T>Pa;Sv*y_4K79C4U~SPww55m$ z#Tif@X34#&x%vHjRq5AIlJ}K=t(>+td2OxZK0j0qW8OWfqh$MOmJCG(+;UXIP@01U zmC7Yc9MzuU0bS_vcJ#Kn{So8gD5||P6c?V#n271&GG}wof|ZfVh^)NDPVMsG_kVZ+ zg!cL(wC30zw$~R)P=pIzevOhX7D^=y zDt8Lm?pWz+=;1uw9kh?F`VBO^Aoau-EiBJ>SaM^mJV-0PNSbWGp-NxJ(p7_k z0QVr1lPxRy!?#Rx!*yt`qVUSrJQ>Hl9lepJ%3e>1h8}0;PD%*D>ZkPH_b(1_bWZHr zK=Pe9LV8-=CpVLW+HH65b!lAJpz-(pYr(FL@BBSaUMKum?+oIs^?T?y+($?93VD)K z!ws=1iEr0qq$gD9jhqzeqb+G%PL{i;Bm(QT(`Kb0BBND}9U)k2KHE7h*PUkSIq_$$ zJ5^?;p>MdTd|%AM7~O*mMxpmfIjDL2LZMV1QGwz}j-1jAeR-4x#pUjP4_Ylezcqh~ zv!pTWG)~t{iNJxcKB9Mbj#E4#Q(I7VY?EGE7%3SD?!6tjmTa|)RqS`e&!=X+4j@od zdgQUDD0yQQUPVHgaQ09gqFDqqe4KbO^A{>Fei1x}fuy0=0&#Fk58% z^^BcE;&bA`>ch0*_V97ugAJ&_#_(-;U z&8S_~`Z5Rv6~MQzT{l~qdY?9+)*I4VC}Rr82_aYe6rEFwyP<3J3wy76VlNwNihlk#kLP!(b5e_CiZz&z47%BU0 zQ^86ca}-1GfLID&+~9*S#H!3zEJ%;p?MA*|IYgFuL#LlCgrb8n9+!Y+b~(~FLnkjv zAnHR0o-j4FH7uWXJ}L zUNDf9_}WEgkfOHej^hKl0}J|h&p%zOfoj>NN?gJzEcj&h&e$}+?@ps=JuBCnS3#zJ zXK>0s$4y`lMdyH`(((#dJ#2ze7%w3&I@2tJDYW4{_qkSN(ib==|G8Jf!0rUKdyZI#6S?Fm$NEJ z(~#&5-DFjq)(k$$dCruNnO7)~3*gI^kDO>~JD5M>EPLqTGYC$Cq1_`=gLwafezdKC zeYRpS+#-mc#`0Ky#=B;}Dhb%=>bMv!)ZU4_$ z-JEjk*M?*swRr4?_-sNmFsS-r(gRjy*)xbY2Zg4R23yIp$wScDuT*SnsZwfXeibrU zH_#G7{UfF$ZQ3)dq({O9=lQX|R0{#^r2%Cygk_q@to=fscf<#K10S+GI9!_KhDK?O zLV-;yO&^l0=;Jt1p`AQcGpDVk;E>)Zq1D`@q5ebuS(5WTQssZ;;-dF~ zEh=id?aT9u{N@VFv^9$`SC<=vWAIzI_+NmD+pqVc>|v285{-8k)Y=|pDX`WgXdGlc zQT0jyTc>!z!LLkDi4p07>U8P_*9u=E78L#k+{v|GcSN7E-Txhtuk99)J1e+#|Xl-jv%T~%I>H=AC zcTIdDkuskZLS?(nb8B-0VyC8%P5{O)-@kwV>R=H%IiOrj7y%PD5ngF0HqMV{>oIM{s>;^4>#r5z|i;Qw)wN7VRriog#a zAz>(yF*h0(Y1e2YCb4m__w_%cHZN~;a74r#b0_)T@^f;1HGEHU3;}^;y$^o{{wP+t zn6fgfrlzK8A}E0{DQy3*JTw2&Nb|RH_4jV`YAm5Ncxg>tLwySAFSY}_EOzSKv-@X& zOcHvl&)dJu=%0W5XSV-3_0Kc@UFu(-|Ci?WAIJX7`ENadV*F3nPtoo7^9sPxrxq{F0W!6@}r6ott}#63X#cwaU2j z_SLppAeDV}uhXO49wMC5tzEl3>y&a36OMIG1doQ!R???tZSOy%f-_SwnEm8oU*%zv z$*cFIEnK(zuF*eyybcwg@#<{e8njuuOt9BV<3<~Q+gs@%hW7>ggBTxJwe~~MZkAHX z%%la#J(}D+o5D&wfz5JJw)c!}o-;If$>16NIhRS)TNtnX_?6=24e1iQ@~rVrEk!$w z=DcfaLPE`Bt(xWUoDoBsV_>?JaIU^@N#Hr)`kb0LxMb}e_tDL%eSdT1&-@LXrW?!LK3LlTwizm1R|BKI5^Xw-q;o`KmGaQvOuJ)4tC0_PK~u z<~Zf0*6XFtz>VI%Lk1yzxg%?rdPfE(%g`*rpVC&+ltAD{%agT4;Wb*_S_mEyx24z6 zH6(pGP52u+hDWjg;na&%G8&hswwI{mU zQg!L`Rh##QIs^gPgijLUMLsL&9i(ute7dwPxB^MwJ-#nyxOiGzBt5EO)fD znqgU|XwFJA`dnk{z?1P!ESv?O>TWdH$m^%26=@12%MHYC{IO4O1VsU-@&JSMyM5IS zpZ7>N+0)R|T?M|u@kWcXVjqbF8^YqvPRvEFV*K z36jqX=M&u)%D}VL0-iBzA`RdNci+>}-}?Y`NV6LES~;kBC=()iQ41O!Bk@f$QMV-UB6rbt^H7UmAxPGy2AB)J)<66?|C!8$Lh zXXA^Gp9O7qTM?jSJiaO3A>9Er&z_1(L6_xqi9*PL&QI!(Tanvf>UC{-<=|O+e3eY+ z0eM-I#O2)(oS`GDIZdP4wr=tm)BBtB_D5ZK3ZRZy;Y-Gz?r28j*mqxBPeOVw0s!T_ zI$?h$Q?m^|ZnUCns=7BpAK_7p-A$m~#B78B{_Oqeexn)5V#&R$U3ld@g(~0ni4ZZJ zn8xzdl-Rkj6Rx;3r+v2i?qI4&(U&MoCxj!849wRC54NWzM&{b@PFlPO>lsocye`P} zVHQg&_s`vdQPm26c!nfJul5WKr=#tFoeZiEACMKig(waeG;*r$E!{#$Tc2D7*Q&xfx<*ud+9) zD$0)X#}Re)lXYQ??O$#!=gijLkunT6qs;Dhc#0^bvnV%@ejt+dEQ8i6x0T0pHX% z#n!PRw=|;3 z_>g!-$kB34O=P&lv7(cfOKLu3UqX}+UyIw#Yw^uk;44KL&QlH zv9C+&it^JN7+_U)^Qcq3ZY~!{t=NS#*Vn~~K%)JdwS$wdqMPyte_Py5xc`fMgw?7H z#V^LcadP`5Wn{XUQBmPLp8Xq1o9&Q0Sw+Bt0&(V*-vmV_@!*w0s$}WZMaD?lw(+Yc)W&sJ10VDka4!7hhgap!O~gc*Xx)Sl zt1P&`^}d%&YQzT^sdYa==FZ&i4C5b=tTY@ zH$4@T>w`@s0r4Vlb3TJ`^CB-bnYcl=*4v(5bi`c}KqL9@a-Hb((AT9<9_hK@>=5nx zdNFzQA=P^Idh-bIR;vY$}eAz+(q{vmgKRB-g92pID*ywLVF#>t^2X zMAFVne3e;bFq83Lmj4!u6V>l$mi(1CvmfQ*tCCZBK1DZka8wu>n<#WN9z7ttxLE>L zUDPme%yNU0&EGkb;-q7lU~GM3qhj7dR7_0H-3NhL&is9sK5Vy@3}XIb&9qWcPTGV>d{uKM<^;G!M1w2Ze`g{w}b7BA+Sc})ni`( z+tlP9m;mi+K<)cWA7#OJTzCs7$-FewJqGoQM5k_MC%eC^i&5IW-6*EHmbo4sudnpf zW^8^Q?;nrjK~>G?QneRxSS>>tCo(H!Z`9b`J!~?%Z^4VoAR$V|%lzXg-$akUMeNs7 z*#W2ktZxOar_o}NN1@-m6f1hD?e706fBsVp{W}${dlAeR&8Mp4oX`7FRMaq3tp6R9 zvfK?|gmy&Ml8aEj{V|38dwul(GsXH(we+8<|2bj%&-4G8`tRfhCt83aDtZhj&Kky5 zp3mIZ5gblAbh>_)tUF`x;Ef38qOtMXK}sHWue7&#JDmQ~(a}L2XNmngV@h3*z16H# z@`y&w=pH6Vf?SmB1tD|rphjZSdA?&0=?`@Zx~YwM)R2iAI-tz`Gm$q+Bf%8p5&1m?kbpGLlT2IxvV1QQN{;zMOKJ!|G3Ark!wWC{WXe?6=F{+<2MK;C- zNX>N*9X+Y8aiq3?q0wjHh3G~3N6L`$D$3^@{Mr&5F`DedRbR{*kPLs*x0iFZNuV<= zV9jGr@OuNwOo;>I)AO243|GS_-3JxVarBdF&2YpLwUQu9Ns$)KX*P}_mF?8fb zSCu=7QNb5@{FRg${!L&9ae|7bp3TGld(*3VDGkW0;xOWQdo+u|+s!GDuCgPt8wO_m z?TFUn+cGTJo}l|KlS3Yy4%Teil?&zo+BlDSf%)?eNy%>4?-ch|CGNxgGmN%2TR8&X zTsz|GBTI{SCY&@{R*QhmVp?iIWuy~K%b ziY-0BpJ)KTuu}PHu-Zj5RIML!a}JZgSUP5@d%gkS?gySh25Z~d>V7)PXlX9Dr<`l! zbQ|mPApdB?=Z(!4dybJ->y~*l0xn2n&~=xb4YB3n4{3>h_x4m;Y~*zcW9xOV9CpE+ za@lPy=)V5(`5^L@SYTL9R{(CV*vAe^Ud~^@gOj&0v`nJQREiT)7Nx&u*c$5c>yqQ+ z>?OnA=kybtw^l1+^#{L$+UapqhIbqbnG8I0+!q0$z;d>>HB!D9Q*TjeVx-czZzI$yQzN1A%aa-B?l@D!SJ~Hk$QE{c%Qyj1n-<(bs zJEUL@yRIS`Yk`j54xuPrcYf+q^-gVjr7TfgpUiZ{{)^ch{oS=?R>LtSg*JTmUYCZN z!o$+7p)zTHCyC)Q_{cw%htych`Y=Cb&LW3A4X?_(3)6uVT4v&27G)dNkiv&x}Ykmh@T`*!r-Yqx4}Hl zDQn^37X>g}Jh8USqPukS?5vn)x%ic!@r*)5jhTygJ_JQcR2z32;1WF>+bN!6zoy}+ z*6+E6R_zux$u2h;S$K{4je_Jf?3u=KmYrjSTAHr4HwFq-XRJJ80|*IMjOEFjohC^Q zhVjd^=!}ozOL3<%Hdb>h4+xXm66F9BmA8+=LUH-P4ZSOy_g2j|u`(EzhRZEZ=$CjA zl~$*oZm=SgyI<|G?;IJv|CNpzFw~T7P;U z32$t6S;|54hnu1yu{NEXCp2y^L9RFc0azW-pF?VNh~hsyMgKCW{{6@2ApX4lyWPJ# z|DVVHZ8m>j;-6>yyHxa3dn?~l7851o%GpA7uTUb=7kC z{ezo*z%=*cRJO+UWt|ft8wF!vT58H5SwBuxb^jT@jYYpNw@a_?Fr^c8PM%Dy?~cXw zMNL9AZ{NPD#eac+zT+rQF;#ZFUv>g^xc#aiq?P-B$7}@!o6p-nL;Sx?{deO2|J~d& zIJD!04Xwf?+?8JZRrpCopMMLBJ?qteP#@eMGWVZdlYg1|XAkE&Wf)2iSob~3FgVy= z{Dz7Zt>9ax`kocT|HnrUZH#eKipUMwJC|8UTb2%H7FjYGP`G8oW82Zx^z^vPacU^d zpV!5<26j8W_oRoe$+?aDsd9sxos-k}73j?Qca|r{krNwr{vt zjb}AGcu9!aJC3&>0AFMsZ6a^g!cvqz&WBX9-SQ*ORlxAHjv{r&O%G2EOIC-$reAI?~_hi%7M$N@q`GR8@(}@75h@LS9*34rS|2 z*7>THKkhYqkvebX=Q@$_>~#>$BW%k%H>fe&GLIsdl#d}sW!p1l+FM)F5`Gi`Z9;}T z&OP8$T@K!93t{#4ir79nIW09NVy^Z9&#%gsNe_@J_d-r8R+lrX4pQYZA#@L?dN&*G z5Z@dm44F&|qDvSULPaUjkKZUDh;_A)R2?r;DiW&hl7?$=2_1vse0_!?__p=oJE*5z zrXjsQ{k_SF!pf?LDNzQpRjwCmImUwEYOjh=R(G9h#n~HjkaB>@Yw5)#z>5Ub)4tK`iEX#{ zMIazmy~r)?y6O#J?xs81k_<&?}+HhL}tUX&pX8?NN@o_(6%G&Lh65LLPyl2G6OCjGLXV_xK%)dW}!M79< z%g7z|{UZ^hAx%^dTHX09x9V&Sp2VFEwmm}mxi`Fo1rd@+q@M8^N3!}1UOp5{?Y>np z?q*C&13A|(Z+9ELcT9yuXZV5FyqnyGRe?-F<13bfKkw>X*TzI%`gXvrRB zzc&GeMZH32kw0?H=({ZDz6(p>u`=mC@h|6F_YB(StZuqzdI0HU^!NGA8tC9j4p$)$ z&w2ydnkR0|>%fm>&J#D*)^;oMc91j>csQNCEP8at+B-}R1p_SpBu>6!e12>%v?e(v za!aOg>Um&MhyRkq<;E$cKEpd6Ov@Bj=N%Qq$#q`s%$rB%AGlz1eZ0&Mx!L&k?c3P$ zvK)0xR$d-Lzpcx0uS5n$IoK>IQz-%?5O|j_pl7yyk zlG{?d#`OoJCSwKZe!RpHc?QeB}AgGu?2kCK8eXj+q@x_ zt{sauEz&WC;rKp&y~A?iku* z6G+x;7j`D5r^54_0)xu~o6eP27M(+ew!%*F z==1t`6bw!_$Mv#j$!NW{F-N9#Hu?DKD)U%<_<%JqTq|AkGG;Kp2;^=0N;5xzVAwv5 z$GKXh`~Mx!ke#AN~=fO^6$eDEhwbt8Q648YADG#La+s&B4}<`oZANB zYpIprTZ{9}W;yY=U(qwQoWj9Cmk5OpqK7}-FD|z`TNmTBm}BmR&xfI#VWM$+c)Ki5 zK_h=r<05O}@_^kv*2F`V&z$4& zIS;j4Z{;uqd#^aeNcmnEvKlTSsqg~b;dkt)Mqbu`Zmx+Pn)%u?vukjq1yF5j@Ua00 z)hHNoN|()&xs|RUXnSqb8v$l2FFQ}FE~mmC9@1pOhbH<47`O=UJdIqA{5%S;=%3D- z;T;wQXS|LmRC!zEsI=#7ZI&z^*yP~vyP6-)uhD)R7or{P@4K9|UaQtw5T$TgBdo4= z`W-GeCopK#t%7d@DN>5jB5cS=;t}7My8fWBLXf3^eR5NlAm$G3bO_{M-bF%pw$M9T z_n^E);l4hT$8P-2>C|JK=a`%+B-dm?$_8Ed<}Q`5NuhxX1F39hM_6?GMTBVBa1N%x zkwzKSVaq4V8C~pCf7%bsA^kBtAd$OIRHb;us!1j=2(FIBh2PEBKM@aT{;sSIm(!qY zfGz>zkR389ptRK1mWX1a(dgE}Ew_M#oxF`fBeCDT^Uk`O(pu)KUHH$gi)H#Z=BV6imV z4go~n?mU4FAh$YRL&Xs&s_jibFM5r6ivt(F+n{>@llysGZ?LCHj`b|h)X+JMOa>Ib zRaYKy)$&Go$(KQ5tIOqFFgce66LZRSnj}Yq1;5l7BE8$byde!9(9Dhgt2)xDOo17e zhU&>RrO1g`okGiEy!CdXRe{y^4Xuc`8yW$JH+|QQlg!Kr$u8!z{TB@|Qpw5L9(!P< z6>Di&T41_V)ds{1Jv_nV5Fg#k;(XURL%%zz{&t%0>ms%Hh=7Oty*_^-dl|{bz_D|1 zglERMl}Ei-+zE(+dh=*2Cav7A(cMI< zf@2ancD|~j=5Qlq1{8Km?!Vz^E_z%^{FGMaCLalj%_)~#D$k!sw69g__i~Ed#Yu}G z(;U_ef`*}CwZ32PWHe_-j$0NN7v~#X0X52)QCHZZxCOr`GuBOd9W3J+wRm%j@z8Ba zgADp3^2H`r(IOy9&p zK8=@S3oM5CK*tWKU&@4_| zC>JE0-vAxB{NG|o{|Ei9B*qhqlDFhXO7H#%7jN8(K1IVReR)Rwf?xlCL4Rlt|LhO^ zHx!S}>K=D0o#85akNAMm^L6m;savZdE0JO$ZlOcqH(oj3H05IG_>*UfdUhkLpx~zs zclZ(ae9F;JJ5AZB1l{x~{>dJLxSyaDXclxKzucSM;Hf(?r`S@+oCE4AUbd zuzmfBJBAXUB5?)`07%=i1V@6=ZbyauWP;v{`b}pt0_1c%4+?dYUo=YXf#;B?yQnUxo zg9KaBRVk%h)o4cu)Y&DVT$~~Glfsp{Xb!LiY3!!}p(M8r&qgk_koUscfXVH)mJdQz zI}bdv@n;->IaZi6Fa;*R zF*Z3!Q_!=ek8-}_Ua{K~#(#rtLcBR{u_c--W; zRad=L|M`7|^arUZvkp;KF)wa^qszUE=h=~v!;3xkbQz)~&jY}?QY7Pos<)G7PMu-APQO5-->eBAJ_mj!7r&Cx6W;d=i=E_9w=7Jh_ zFe2_<7M-9-^*aOf5@v{|FZ*m2Yra$3Gl?RKe00mMOig3cZpAA$=79K6P^D2fS`%2g zZ}>-z&T)FRKhyRDs4*8MARcTCIR_uWC0y}nWl?Jx3UHXTHnXOCPi%`8C2h>cDq z{;OR5tEX)(_3cvvWbw>jjt60Tyh|MczUC#tO5%^nm<$>0<9tC;j>ZC_7(IxmiN?UM z6GU5KXD>Yn13%ui5@ZTpCvoxTNm0j)%RiT;IFVa?OfZ(-fdR(pmU>DbJmRfJiriVK z+i{ySpV2m>*Mk*7-2r-_48d%0#nu{MBf}ifynSwT&)+>}`fbOrW{P#j1I+SAh}^<2 z#yp`Cz@g;Gha7H>2*k!?zuq<4 zIyHAIYy^{&Ywg=?MtN_QC$Mp8b+A&>&8aSTHn*E)TH3r=mJ%P-rUdq9Gho85OnwHu!=m(M-&t{wsV6aLNW^6X_3dyf$Lz3Vf?=3iU6Jkg zI0c;na2`IB?!Vd}FJC2C2f`(}#u79;q#pdPsEd7wCu~KZ$J&Z>?O7K@?t%`Ql3Hx6 zX1_MFVB>D&#T5cZ%Va=Qh^i^FCyC&)mUh;wA$LzUK85L( zqZ=$Cxg5_U4Oy#S3p9ga_{9QZlNfiHG^tr?jvYvL)Y+C=tA4(Glkn9~XV3k^Ecmhs z&U6VN6O=Oy3HRU2cyewMqGu12e!p*)%fYi^YV#}X{;9`WXkeAtb?JN#eI_z6I0_mlJVO|mT8PzgFZlB_9>&=l>%F(Z@~BH|7FehY+2{>+$5C6J525O!+FT zOGgN{elq!+^ms_=^mZa5A~Va-vz)$M2B&Haj&cvJ-8BZ6jtG2az8|7#tm-;8m7e4; z?aobClc8xT&UiuA3{$yg6YCcSaWgKFd_P^@!5Snka<(;z>q4rtgI=qY050D0N{TL@ z;8fJc1#H{KDzBn4uaG+)c?=gYye6KDAq>}L=ro0MGemnREGs0{b&Q0Yq^m1MRe2%a zo=!4=o9?GKmThypzl$EXnus6z?A*(S4^g^08K^9w!0e+hznFq}CrzojvE{QKqSNU# zi9~{+-_JmD{3C@-$x=K%i*)IVNkeqd?jjE+??> zEO7vD2}>%xxUp_p`yE^}8?MtQ-r;}m3G?(*TiO1=N)GbcZ71j|> zj|p(06?gN6Ma}&FwWIXf7Aqh6CN)C~Z5!Ji8^*`G1BPj0w;tf-Kb>aOO9WTX?c!KP zloxskMpm+^0x&#yJ*AZRI=SqUy%ge=t%Ol`b1WMUmff`VFQl}N}PrZIeKoslOw%Cf_tD+mqrISrh{()RboR$-vv_mb zPq6n)lF;fq`2H8ybH}Y~dGu@daYtc>=8qa^%#66ei{p^#ILb@!cjB$x#Fy3$j;_wu zZH6_;l|RPboSu^$Zm;5wyH-%-9el1~VR^s`2C#d&h-O?|e4*|8siZV7Mtk?%ncnNo#{OA8LbYu= zI=d(;Dx@dcf7Nnd^GDG9(WL8%ghF(5cI7U2KB_+kg6A1&8@4=c(3;h#Nu(1$s|QZg%ssS zNM|x+Y@2i@gKax_JSLXci`V19@_I=olU%&mswo^IA|kVtpA{J8=1ku~DpxF}GHBD$ zcshF}%(?Q`j4>hFFs{!FWo(=Fj!rDg!e?3dEH6Qy4{y**CX*qRO3@!5AeBz*EF2;t zBC{K#XMaxg&0i87SlkHdb{Y=OVA*gx4bXb>D6Ml~Q^w24-WyuW!H^tNw-ON%xq+ck zf${2+s3vl4yeff+h{*g8(DN}gdd-<`my2N-^Rgz!HMI47t%+%x*JZmDqD4eR=CMpB zL%?SePh{rZwv`w7NG7L$`MOj(gU2*UXXal`0zPX-HA$y4_$-TLDmCwFk{1ros3wyg zu9#PjKD46|5fPbh(#bdri+p%Zz;))u&1<++75ga3_tVqYH|?6l)Mfs6=SCjS`)}$}mPjO)Z3oFW1lbnYtV?wlH z=r;!$-Gy_dvI5p_-^B*BqlqTjK$LqtSGL}aoUJtixg<0otp5fKp)880gv#^8CPhfZap z(P$A75fPCw@!7Z%o`>j%g(4y%A|lfa)Wbh4G?b=AL_|bHCJ4_%S$V_{3zeTmL_|bH zCJWM6{?)}+L_|bHM5Y#_hyV2&hJow4!$Z^u_wO5i*p*9xyE*@xh=_=Y$jusxN6*R_ z|AZS?PQ;P@*E{qe*M$WO77T}EckmB)I5oSY0<=3SLyx^b5e07%5fKr&et|gWBaG3H z8+m{9-(&yfs$lKC@}e$aU>MmzJbHxM#dVFrCyxAky!*LAv!;!Rh=_>Drx&WHAEZY< z{>n1%&?xj!XwC*{rWBw@K7Rc0Y)o;4T0}%dL}ZrYvoV!0J@p`cb*KjKl~L%CL3;d9 zeI+no8Lb{KJiD$j!A6LPh=_=M8iCrFV3{#Nupa;Kp~rhC4$q7jiN5mWt4E)Qg7eq} oFCro$A~N>_^+co6xx)1S2RtPLy{PbUmjD0&07*qoM6N<$f=#1rH2?qr diff --git a/docs/cli.rst b/docs/cli.rst index 1a6da62f..a55ee73e 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -343,40 +343,51 @@ script is available. Note that you don't need to set ``FLASK_APP``. :: PyCharm Integration ------------------- -The new Flask CLI features aren't yet fully integrated into the PyCharm IDE, -so we have to do a few tweaks to get them working smoothly. These instructions -should be similar for any other IDE you might want to use. +The Flask CLI features aren't yet fully integrated into the PyCharm IDE, +so we have to do a few tweaks to get them working smoothly. These +instructions should be similar for any other IDE you might want to use. -In PyCharm, with your project open, click on *Run* from the menu bar and go to -*Edit Configurations*. You'll be greeted by a screen similar to this: +In PyCharm, with your project open, click on *Run* from the menu bar and +go to *Edit Configurations*. You'll be greeted by a screen similar to +this: .. image:: _static/pycharm-runconfig.png - :align: center - :class: screenshot - :alt: screenshot of pycharm's run configuration settings + :align: center + :class: screenshot + :alt: screenshot of pycharm's run configuration settings -There's quite a few options to change, but once we've done it for one command, -we can easily copy the entire configuration and make a single tweak to give us -access to other commands, including any custom ones you may implement yourself. +There's quite a few options to change, but once we've done it for one +command, we can easily copy the entire configuration and make a single +tweak to give us access to other commands, including any custom ones you +may implement yourself. -Since PyCharm 2017.3, you can run modules instead of needing to find ``flask`` -executable. For the *Module name* input (**A**), you just need to input ``flask``. +Click the + (*Add New Configuration*) button and select *Python*. Give +the configuration a good descriptive name such as "Run Flask Server". +For the ``flask run`` command, check "Single instance only" since you +can't run the server more than once at the same time. -The *Parameters* field (**B**) is set to the CLI command you to execute. -In this example we use ``run``, which will run the development server. +Select *Module name* from the dropdown (**A**) then input ``flask``. -You can skip this next step if you're using :ref:`dotenv`. We need to add an -environment variable (**C**) to identify our application. Click on the browse -button and add an entry with ``FLASK_APP`` on the left and the name of the -Python file or package on the right (``app.py`` for example). +The *Parameters* field (**B**) is set to the CLI command to execute +(with any arguments). In this example we use ``run``, which will run +the development server. -Next we need to set the working directory (**D**) to be the same folder where -our application file or package resides. PyCharm changed it to the directory -with the ``flask`` executable when we selected it earlier, which is incorrect. +You can skip this next step if you're using :ref:`dotenv`. We need to +add an environment variable (**C**) to identify our application. Click +on the browse button and add an entry with ``FLASK_APP`` on the left and +the Python import or file on the right (``hello`` for example). -Finally, untick the *PYTHONPATH* options (**E**) and give the configuration a -good descriptive name, such as "Run Flask Server", and click *Apply*. +Next we need to set the working directory (**D**) to be the folder where +our application resides. -Now that we have a configuration which runs ``flask run`` from within PyCharm, -we can simply copy that configuration and alter the *Script* argument +If you have installed your project as a package in your virtualenv, you +may untick the *PYTHONPATH* options (**E**). This will more accurately +match how you deploy the app later. + +Click *Apply* to save the configuration, or *OK* to save and close the +window. Select the configuration in the main PyCharm window and click +the play button next to it to run the server. + +Now that we have a configuration which runs ``flask run`` from within +PyCharm, we can copy that configuration and alter the *Script* argument to run a different CLI command, e.g. ``flask shell``. diff --git a/tox.ini b/tox.ini index 45d81b65..1a9c16fb 100644 --- a/tox.ini +++ b/tox.ini @@ -40,11 +40,11 @@ commands = [testenv:docs-html] deps = sphinx -commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html +commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs {envtmpdir}/html [testenv:docs-linkcheck] deps = sphinx -commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/linkcheck +commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs {envtmpdir}/linkcheck [testenv:coverage-report] deps = coverage From 0a339545551b5303fb2750e6e5b0e72acbea60d4 Mon Sep 17 00:00:00 2001 From: David Lord Date: Thu, 4 Jan 2018 08:40:12 -0800 Subject: [PATCH 377/399] improve documentation for session attributes add test for session attributes --- flask/sessions.py | 63 ++++++++++++++++++++++++++------------------- tests/test_basic.py | 11 +++++++- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/flask/sessions.py b/flask/sessions.py index 82b588bc..8f111718 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -10,6 +10,7 @@ """ import hashlib import warnings +from collections import MutableMapping from datetime import datetime from itsdangerous import BadSignature, URLSafeTimedSerializer @@ -19,43 +20,55 @@ from flask.helpers import is_ip, total_seconds from flask.json.tag import TaggedJSONSerializer -class SessionMixin(object): - """Expands a basic dictionary with an accessors that are expected - by Flask extensions and users for the session. - """ +class SessionMixin(MutableMapping): + """Expands a basic dictionary with session attributes.""" - def _get_permanent(self): + @property + def permanent(self): + """This reflects the ``'_permanent'`` key in the dict.""" return self.get('_permanent', False) - def _set_permanent(self, value): + @permanent.setter + def permanent(self, value): self['_permanent'] = bool(value) - #: this reflects the ``'_permanent'`` key in the dict. - permanent = property(_get_permanent, _set_permanent) - del _get_permanent, _set_permanent - - #: some session backends can tell you if a session is new, but that is - #: not necessarily guaranteed. Use with caution. The default mixin - #: implementation just hardcodes ``False`` in. + #: Some implementations can detect whether a session is newly + #: created, but that is not guaranteed. Use with caution. The mixin + # default is hard-coded ``False``. new = False - #: for some backends this will always be ``True``, but some backends will - #: default this to false and detect changes in the dictionary for as - #: long as changes do not happen on mutable structures in the session. - #: The default mixin implementation just hardcodes ``True`` in. + #: Some implementations can detect changes to the session and set + #: this when that happens. The mixin default is hard coded to + #: ``True``. 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. + #: Some implementations can detect when session data is read or + #: written and set this when that happens. The mixin default is hard + #: coded to ``True``. accessed = True class SecureCookieSession(CallbackDict, SessionMixin): - """Base class for sessions based on signed cookies.""" + """Base class for sessions based on signed cookies. + + This session backend will set the :attr:`modified` and + :attr:`accessed` attributes. It cannot reliably track whether a + session is new (vs. empty), so :attr:`new` remains hard coded to + ``False``. + """ + + #: When data is changed, this is set to ``True``. Only the session + #: dictionary itself is tracked; if the session contains mutable + #: data (for example a nested dict) then this must be set to + #: ``True`` manually when modifying that data. The session cookie + #: will only be written to the response if this is ``True``. + modified = False + + #: When data is read or written, this is set to ``True``. Used by + # :class:`.SecureCookieSessionInterface` to add a ``Vary: Cookie`` + #: header, which allows caching proxies to cache different pages for + #: different users. + accessed = False def __init__(self, initial=None): def on_update(self): @@ -63,8 +76,6 @@ class SecureCookieSession(CallbackDict, SessionMixin): self.accessed = True super(SecureCookieSession, self).__init__(initial, on_update) - self.modified = False - self.accessed = False def __getitem__(self, key): self.accessed = True diff --git a/tests/test_basic.py b/tests/test_basic.py index 0e3076df..d7406baf 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -221,12 +221,21 @@ def test_endpoint_decorator(app, client): def test_session(app, client): @app.route('/set', methods=['POST']) def set(): + assert not flask.session.accessed + assert not flask.session.modified flask.session['value'] = flask.request.form['value'] + assert flask.session.accessed + assert flask.session.modified return 'value set' @app.route('/get') def get(): - return flask.session['value'] + assert not flask.session.accessed + assert not flask.session.modified + v = flask.session.get('value', 'None') + assert flask.session.accessed + assert not flask.session.modified + return v assert client.post('/set', data={'value': '42'}).data == b'value set' assert client.get('/get').data == b'42' From 06f96df67e291caeaeb09ad296aa368c80cc1a58 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 5 Jan 2018 07:40:51 -0800 Subject: [PATCH 378/399] silence ENOTDIR when loading config file --- CHANGES | 8 +++++++- flask/config.py | 4 +++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CHANGES b/CHANGES index 11fb7df2..3517f1a7 100644 --- a/CHANGES +++ b/CHANGES @@ -3,6 +3,7 @@ Flask Changelog Here you can see the full list of changes between each Flask release. + Version 0.13 ------------ @@ -114,7 +115,9 @@ Major release, unreleased depending on ``app.debug``. No handlers are removed, and a handler is only added if no handlers are already configured. (`#2436`_) - Blueprint view function name may not contain dots. (`#2450`_) -- The dev server now uses threads by default. +- The dev server now uses threads by default. (`#2529`_) +- Loading config files with ``silent=True`` will ignore ``ENOTDIR`` + errors. (`#2581`_) .. _pallets/meta#24: https://github.com/pallets/meta/issues/24 .. _#1421: https://github.com/pallets/flask/issues/1421 @@ -149,6 +152,9 @@ Major release, unreleased .. _#2430: https://github.com/pallets/flask/pull/2430 .. _#2436: https://github.com/pallets/flask/pull/2436 .. _#2450: https://github.com/pallets/flask/pull/2450 +.. _#2529: https://github.com/pallets/flask/pull/2529 +.. _#2581: https://github.com/pallets/flask/pull/2581 + Version 0.12.3 -------------- diff --git a/flask/config.py b/flask/config.py index 697add71..f73a4232 100644 --- a/flask/config.py +++ b/flask/config.py @@ -129,7 +129,9 @@ class Config(dict): with open(filename, mode='rb') as config_file: exec(compile(config_file.read(), filename, 'exec'), d.__dict__) except IOError as e: - if silent and e.errno in (errno.ENOENT, errno.EISDIR): + if silent and e.errno in ( + errno.ENOENT, errno.EISDIR, errno.ENOTDIR + ): return False e.strerror = 'Unable to load configuration file (%s)' % e.strerror raise From ffca68fc86718b133668f341ddd8c2635d7e29ec Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 5 Jan 2018 12:36:01 -0800 Subject: [PATCH 379/399] fix windows failure to remove temp file --- examples/flaskr/tests/test_flaskr.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/examples/flaskr/tests/test_flaskr.py b/examples/flaskr/tests/test_flaskr.py index b5ade2ec..e1e9a4e7 100644 --- a/examples/flaskr/tests/test_flaskr.py +++ b/examples/flaskr/tests/test_flaskr.py @@ -17,33 +17,25 @@ from flaskr.blueprints.flaskr import init_db @pytest.fixture -def app(request): - - db_fd, temp_db_location = tempfile.mkstemp() +def app(): + db_fd, db_path = tempfile.mkstemp() config = { - 'DATABASE': temp_db_location, + 'DATABASE': db_path, 'TESTING': True, - 'DB_FD': db_fd } - app = create_app(config=config) with app.app_context(): init_db() yield app + os.close(db_fd) + os.unlink(db_path) -@pytest.fixture -def client(request, app): - - client = app.test_client() - - def teardown(): - os.close(app.config['DB_FD']) - os.unlink(app.config['DATABASE']) - request.addfinalizer(teardown) - return client +@pytest.fixture +def client(app): + return app.test_client() def login(client, username, password): From 333865ea34acefbca565170881ff403fd7840d57 Mon Sep 17 00:00:00 2001 From: David Lord Date: Fri, 5 Jan 2018 13:27:31 -0800 Subject: [PATCH 380/399] windows python 2 doesn't provide inet_pton --- flask/helpers.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index f1011ad0..067e97cb 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -42,8 +42,7 @@ from jinja2 import FileSystemLoader from .signals import message_flashed from .globals import session, _request_ctx_stack, _app_ctx_stack, \ current_app, request -from ._compat import string_types, text_type - +from ._compat import string_types, text_type, PY2 # sentinel _missing = object() @@ -1002,12 +1001,21 @@ def total_seconds(td): def is_ip(value): """Determine if the given string is an IP address. + Python 2 on Windows doesn't provide ``inet_pton``, so this only + checks IPv4 addresses in that environment. + :param value: value to check :type value: str :return: True if string is an IP address :rtype: bool """ + if PY2 and os.name == 'nt': + try: + socket.inet_aton(value) + return True + except socket.error: + return False for family in (socket.AF_INET, socket.AF_INET6): try: From 22e072379b9fcffd229cdd239dd3ff5ed4eed571 Mon Sep 17 00:00:00 2001 From: Hsiaoming Yang Date: Sat, 6 Jan 2018 18:49:01 +0900 Subject: [PATCH 381/399] cleanup werkzeug import (#2582) --- flask/helpers.py | 13 ++----------- flask/testing.py | 6 +----- 2 files changed, 3 insertions(+), 16 deletions(-) diff --git a/flask/helpers.py b/flask/helpers.py index 067e97cb..412d9caf 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -22,21 +22,12 @@ import unicodedata from werkzeug.routing import BuildError from functools import update_wrapper -try: - from werkzeug.urls import url_quote -except ImportError: - from urlparse import quote as url_quote - +from werkzeug.urls import url_quote from werkzeug.datastructures import Headers, Range from werkzeug.exceptions import BadRequest, NotFound, \ RequestedRangeNotSatisfiable -# this was moved in 0.7 -try: - from werkzeug.wsgi import wrap_file -except ImportError: - from werkzeug.utils import wrap_file - +from werkzeug.wsgi import wrap_file from jinja2 import FileSystemLoader from .signals import message_flashed diff --git a/flask/testing.py b/flask/testing.py index f29c6b17..730ad61d 100644 --- a/flask/testing.py +++ b/flask/testing.py @@ -15,11 +15,7 @@ from contextlib import contextmanager from werkzeug.test import Client, EnvironBuilder from flask import _request_ctx_stack from flask.json import dumps as json_dumps - -try: - from werkzeug.urls import url_parse -except ImportError: - from urlparse import urlsplit as url_parse +from werkzeug.urls import url_parse def make_test_environ_builder( From 60eecb547d8af9916c55163c7356999ca2d5ffb9 Mon Sep 17 00:00:00 2001 From: Hsiaoming Yang Date: Sat, 6 Jan 2018 18:49:50 +0900 Subject: [PATCH 382/399] Remove python 2.6 and 3.3 everywhere. (#2583) --- AUTHORS | 1 + CONTRIBUTING.rst | 4 ++-- docs/deploying/fastcgi.rst | 2 +- setup.py | 2 -- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/AUTHORS b/AUTHORS index cbab2a8c..3237ea65 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,6 +21,7 @@ Patches and Suggestions - Edmond Burnett - Florent Xicluna - Georg Brandl +- Hsiaoming Yang @lepture - Jeff Widman @jeffwidman - Joshua Bronson @jab - Justin Quick diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f6ff7015..ef02b732 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -109,8 +109,8 @@ depends on which part of Flask you're working on. Travis-CI will run the full suite when you submit your pull request. The full test suite takes a long time to run because it tests multiple -combinations of Python and dependencies. You need to have Python 2.6, 2.7, 3.3, -3.4, 3.5 3.6, and PyPy 2.7 installed to run all of the environments. Then run:: +combinations of Python and dependencies. You need to have Python 2.7, 3.4, +3.5 3.6, and PyPy 2.7 installed to run all of the environments. Then run:: tox diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst index 5ca2a084..46706033 100644 --- a/docs/deploying/fastcgi.rst +++ b/docs/deploying/fastcgi.rst @@ -111,7 +111,7 @@ Set yourapplication.fcgi:: #!/usr/bin/python #: optional path to your local python site-packages folder import sys - sys.path.insert(0, '/lib/python2.6/site-packages') + sys.path.insert(0, '/lib/python/site-packages') from flup.server.fcgi import WSGIServer from yourapplication import app diff --git a/setup.py b/setup.py index bb2aab41..bf2d0bc6 100644 --- a/setup.py +++ b/setup.py @@ -95,10 +95,8 @@ setup( 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python :: 2', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', From 2433522d2967b8a5e46f16de587a8fac5088a47c Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Sat, 6 Jan 2018 17:07:56 +0100 Subject: [PATCH 383/399] Add Support for FLASK_ENV (#2570) This introduces environments to Flask --- docs/cli.rst | 15 +++++++- docs/config.rst | 43 +++++++++++++++------- docs/patterns/packages.rst | 6 +-- docs/quickstart.rst | 9 +++-- docs/server.rst | 16 ++++++-- docs/tutorial/packaging.rst | 6 ++- flask/app.py | 73 +++++++++++++++++++++---------------- flask/cli.py | 29 +++++++++------ flask/helpers.py | 14 ++++++- tests/test_helpers.py | 17 +++++++-- 10 files changed, 154 insertions(+), 74 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 9e558ece..55d09963 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -116,6 +116,16 @@ context will be active, and the app instance will be imported. :: Use :meth:`~Flask.shell_context_processor` to add other automatic imports. +Environments +------------ + +.. versionadded:: 1.0 + +The environment in which the Flask app should run is set by the +:envvar:`FLASK_ENV` environment variable. If not set it defaults to +``production``. The other default environment which is known is +``development``. If the env is set to ``development`` the debug mode is +for instance automatically enabled. Debug Mode ---------- @@ -123,11 +133,14 @@ Debug Mode Set the :envvar:`FLASK_DEBUG` environment variable to override the application's :attr:`~Flask.debug` flag. The value ``1`` enables it, ``0`` disables it. Forcing the debug flag on also enables the debugger and reloader -when running the development server. :: +when running the development server. + +:: $ FLASK_DEBUG=1 flask run * Serving Flask app "hello" * Forcing debug mode on + * Env production * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with inotify reloader * Debugger is active! diff --git a/docs/config.rst b/docs/config.rst index 6ed27a83..e8da82d1 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -27,35 +27,52 @@ The :attr:`~flask.Flask.config` is actually a subclass of a dictionary and can be modified just like any dictionary:: app = Flask(__name__) - app.config['DEBUG'] = True + app.config['TESTING'] = True Certain configuration values are also forwarded to the :attr:`~flask.Flask` object so you can read and write them from there:: - app.debug = True + app.testing = True To update multiple keys at once you can use the :meth:`dict.update` method:: app.config.update( - DEBUG=True, + TESTING=True, SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/' ) -.. admonition:: Debug Mode with the ``flask`` Script +Environment and Debug Features +------------------------------ - If you use the :command:`flask` script to start a local development - server, to enable the debug mode, you need to export the ``FLASK_DEBUG`` - environment variable before running the server:: +Some values are special in that they can show unexpected behavior when +changed late. In particular that applies to the Flask environment and +debug mode. - $ export FLASK_DEBUG=1 - $ flask run +If you use the :command:`flask` script to start a local development server +for instance you should tell Flask that you want to work in the +development environment. For safety reasons we default the flask +environment to production mode instead of development. This is done +because development mode can turn on potentially unsafe features such as +the debugger by default. - (On Windows you need to use ``set`` instead of ``export``). +To control the environment and such fundamental features Flask provides +the two environment variables :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG`. +In versions of Flask older than 1.0 the :envvar:`FLASK_ENV` environment +variable did not exist. - ``app.debug`` and ``app.config['DEBUG']`` are not compatible with -   the :command:`flask` script. They only worked when using ``Flask.run()`` - method. +The most common way to switch Flask to development mode is to tell it to +work on the ``development`` environment:: + +$ export FLASK_ENV=development +$ flask run + +(On Windows you need to use ``set`` instead of ``export``). + +While you can attempt to flip the environment and debug flag separately in +the Flask config from the config file this is strongly discouraged as +those flags are often loaded early and changing them late might not apply +to all systems and extensions. Builtin Configuration Values ---------------------------- diff --git a/docs/patterns/packages.rst b/docs/patterns/packages.rst index cc149839..6b0ee7ad 100644 --- a/docs/patterns/packages.rst +++ b/docs/patterns/packages.rst @@ -65,10 +65,10 @@ that tells Flask where to find the application instance:: export FLASK_APP=yourapplication If you are outside of the project directory make sure to provide the exact -path to your application directory. Similarly you can turn on "debug -mode" with this environment variable:: +path to your application directory. Similarly you can turn on the +development features like this:: - export FLASK_DEBUG=true + export FLASK_ENV=development In order to install and run the application you need to issue the following commands:: diff --git a/docs/quickstart.rst b/docs/quickstart.rst index d3f3dea0..284f6d76 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -130,13 +130,16 @@ 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. -To enable debug mode you can export the ``FLASK_DEBUG`` environment variable +To enable all development features (and to disable the debug mode) you can +export the ``FLASK_ENV`` environment variable and set it to +``development`` before running the server:: - $ export FLASK_DEBUG=1 + $ export FLASK_ENV=development $ flask run -(On Windows you need to use ``set`` instead of ``export``). +(On Windows you need to use ``set`` instead of ``export`` and on Flask +versions older than 1.0 you need to export ``FLASK_DEBUG=1`` instead). This does the following things: diff --git a/docs/server.rst b/docs/server.rst index f8332ebf..7e03d8df 100644 --- a/docs/server.rst +++ b/docs/server.rst @@ -12,12 +12,13 @@ but you can also continue using the :meth:`Flask.run` method. Command Line ------------ -The :command:`flask` command line script (:ref:`cli`) is strongly recommended for -development because it provides a superior reload experience due to how it -loads the application. The basic usage is like this:: +The :command:`flask` command line script (:ref:`cli`) is strongly +recommended for development because it provides a superior reload +experience due to how it loads the application. The basic usage is like +this:: $ export FLASK_APP=my_application - $ export FLASK_DEBUG=1 + $ export FLASK_ENV=development $ flask run This will enable the debugger, the reloader and then start the server on @@ -29,6 +30,13 @@ disabled:: $ flask run --no-reload +.. note:: + + On older Flask version (before 1.0) the :envvar:`FLASK_ENV` + environment variable is not supported and you need to enable the + debug mode separately by setting the :envvar:`FLASK_DEBUG` environment + variable to ``1``. + In Code ------- diff --git a/docs/tutorial/packaging.rst b/docs/tutorial/packaging.rst index 5db921aa..13e11d5c 100644 --- a/docs/tutorial/packaging.rst +++ b/docs/tutorial/packaging.rst @@ -78,11 +78,13 @@ With that out of the way, you should be able to start up the application. Do this on Mac or Linux with the following commands in ``flaskr/``:: export FLASK_APP=flaskr - export FLASK_DEBUG=true + export FLASK_ENV=development flask run (In case you are on Windows you need to use ``set`` instead of ``export``). -The :envvar:`FLASK_DEBUG` flag enables or disables the interactive debugger. +The :envvar:`FLASK_ENV` flag if set to ``development`` turns on all +development features such as enabling the interactive debugger. + *Never leave debug mode activated in a production system*, because it will allow users to execute code on the server! diff --git a/flask/app.py b/flask/app.py index 88ca433c..e84e0a3f 100644 --- a/flask/app.py +++ b/flask/app.py @@ -27,7 +27,7 @@ from .config import Config, ConfigAttribute from .ctx import AppContext, RequestContext, _AppCtxGlobals from .globals import _request_ctx_stack, g, request, session from .helpers import _PackageBoundObject, \ - _endpoint_from_view_func, find_package, get_debug_flag, \ + _endpoint_from_view_func, find_package, get_env, get_debug_flag, \ get_flashed_messages, locked_cached_property, url_for from .logging import create_logger from .sessions import SecureCookieSessionInterface @@ -196,15 +196,6 @@ class Flask(_PackageBoundObject): #: .. versionadded:: 0.11 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 - #: the application if changes in the code are detected. - #: - #: This attribute can also be configured from the config with the ``DEBUG`` - #: configuration key. Defaults to ``False``. - debug = ConfigAttribute('DEBUG') - #: The testing flag. Set this to ``True`` to enable the test mode of #: Flask extensions (and in the future probably also Flask itself). #: For example this might activate test helpers that have an @@ -278,7 +269,8 @@ class Flask(_PackageBoundObject): #: Default configuration parameters. default_config = ImmutableDict({ - 'DEBUG': get_debug_flag(default=False), + 'ENV': None, + 'DEBUG': None, 'TESTING': False, 'PROPAGATE_EXCEPTIONS': None, 'PRESERVE_CONTEXT_ON_EXCEPTION': None, @@ -647,7 +639,10 @@ class Flask(_PackageBoundObject): root_path = self.root_path if instance_relative: root_path = self.instance_path - return self.config_class(root_path, self.default_config) + defaults = dict(self.default_config) + defaults['ENV'] = get_env() + defaults['DEBUG'] = get_debug_flag() + return self.config_class(root_path, defaults) def auto_find_instance_path(self): """Tries to locate the instance path if it was not provided to the @@ -790,25 +785,38 @@ class Flask(_PackageBoundObject): rv.update(processor()) return rv - def _reconfigure_for_run_debug(self, debug): - """The ``run`` commands will set the application's debug flag. Some - application configuration may already be calculated based on the - previous debug value. This method will recalculate affected values. - - Called by the :func:`flask.cli.run` command or :meth:`Flask.run` - method if the debug flag is set explicitly in the call. - - :param debug: the new value of the debug flag - - .. versionadded:: 1.0 - Reconfigures ``app.jinja_env.auto_reload``. - """ - self.debug = debug + #: The environment value. This is typically set from outside the + #: process by setting the `FLASK_ENV` environment variable and can be + #: used to quickly switch between different environments like + #: `production` and `development`. If not set this defaults to + #: `production`. + env = ConfigAttribute('ENV') + + def _get_debug(self): + return self.config['DEBUG'] + def _set_debug(self, value): + self._set_debug_value(value) + + #: The debug flag. If this is ``True`` it enables debugging of the + #: application. In debug mode the debugger will kick in when an + #: unhandled exception occurs and the integrated server will + #: automatically reload the application if changes in the code are + #: detected. + #: + #: This value should only be configured by the :envvar:`FLASK_DEBUG` + #: environment variable. Changing it by other means will not yield + #: consistent results. The default value depends on the Flask + #: environment and will be true for the development environment and false + #: otherwise. + debug = property(_get_debug, _set_debug) + del _get_debug, _set_debug + + def _set_debug_value(self, value): + self.config['DEBUG'] = value self.jinja_env.auto_reload = self.templates_auto_reload - def run( - self, host=None, port=None, debug=None, load_dotenv=True, **options - ): + def run(self, host=None, port=None, debug=None, + load_dotenv=True, **options): """Runs the application on a local development server. Do not use ``run()`` in a production setting. It is not intended to @@ -872,11 +880,11 @@ class Flask(_PackageBoundObject): load_dotenv() if debug is not None: - self._reconfigure_for_run_debug(bool(debug)) + self.debug = bool(debug) _host = '127.0.0.1' _port = 5000 - server_name = self.config.get("SERVER_NAME") + server_name = self.config.get('SERVER_NAME') sn_host, sn_port = None, None if server_name: @@ -1055,7 +1063,8 @@ class Flask(_PackageBoundObject): return iter(self._blueprint_order) @setupmethod - def add_url_rule(self, rule, endpoint=None, view_func=None, provide_automatic_options=None, **options): + def add_url_rule(self, rule, endpoint=None, view_func=None, + provide_automatic_options=None, **options): """Connects a URL rule. Works exactly like the :meth:`route` decorator. If a view_func is provided it will be registered with the endpoint. diff --git a/flask/cli.py b/flask/cli.py index 1be7957c..2adadcc7 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -25,7 +25,7 @@ import click from . import __version__ from ._compat import getargspec, iteritems, reraise from .globals import current_app -from .helpers import get_debug_flag +from .helpers import get_debug_flag, get_env try: import dotenv @@ -341,9 +341,8 @@ class ScriptInfo(object): else: for path in ('wsgi.py', 'app.py'): import_name = prepare_import(path) - app = locate_app( - self, import_name, None, raise_if_not_found=False - ) + app = locate_app(self, import_name, None, + raise_if_not_found=False) if app: break @@ -357,8 +356,10 @@ class ScriptInfo(object): debug = get_debug_flag() + # Update the app's debug flag through the descriptor so that other + # values repopulate as well. if debug is not None: - app._reconfigure_for_run_debug(debug) + app.debug = debug self._loaded_app = app return app @@ -432,10 +433,8 @@ class FlaskGroup(AppGroup): from :file:`.env` and :file:`.flaskenv` files. """ - def __init__( - self, add_default_commands=True, create_app=None, - add_version_option=True, load_dotenv=True, **extra - ): + def __init__(self, add_default_commands=True, create_app=None, + add_version_option=True, load_dotenv=True, **extra): params = list(extra.pop('params', None) or ()) if add_version_option: @@ -610,6 +609,13 @@ def run_command(info, host, port, reload, debugger, eager_loading, """ from werkzeug.serving import run_simple + if get_env() == 'production': + click.secho('Warning: Detected a production environment. Do not ' + 'use `flask run` for production use.', + fg='red') + click.secho('Use a production ready WSGI server instead', + dim=True) + debug = get_debug_flag() if reload is None: reload = bool(debug) @@ -629,6 +635,7 @@ def run_command(info, host, port, reload, debugger, eager_loading, # we won't print anything. if info.app_import_path is not None: print(' * Serving Flask app "%s"' % info.app_import_path) + print(' * Env %s' % get_env()) if debug is not None: print(' * Forcing debug mode %s' % (debug and 'on' or 'off')) @@ -649,11 +656,11 @@ def shell_command(): 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' % ( + banner = 'Python %s on %s\nApp: %s [%s]\nInstance: %s' % ( sys.version, sys.platform, app.import_name, - app.debug and ' [debug]' or '', + app.env, app.instance_path, ) ctx = {} diff --git a/flask/helpers.py b/flask/helpers.py index 412d9caf..705ea3e1 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -46,10 +46,20 @@ _os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep] if sep not in (None, '/')) -def get_debug_flag(default=None): +def get_env(): + val = os.environ.get('FLASK_ENV') + if not val: + val = 'production' + return val + + +def get_debug_flag(): val = os.environ.get('FLASK_DEBUG') if not val: - return default + env = get_env() + if env == 'development': + return True + return False return val.lower() not in ('0', 'false', 'no') diff --git a/tests/test_helpers.py b/tests/test_helpers.py index d497c40d..1ddde116 100644 --- a/tests/test_helpers.py +++ b/tests/test_helpers.py @@ -21,7 +21,7 @@ from werkzeug.http import http_date, parse_cache_control_header, \ import flask from flask._compat import StringIO, text_type -from flask.helpers import get_debug_flag +from flask.helpers import get_debug_flag, get_env def has_encoding(name): @@ -886,7 +886,7 @@ class TestSafeJoin(object): class TestHelpers(object): @pytest.mark.parametrize('debug, expected_flag, expected_default_flag', [ - ('', None, True), + ('', False, False), ('0', False, False), ('False', False, False), ('No', False, False), @@ -898,7 +898,18 @@ class TestHelpers(object): assert get_debug_flag() is None else: assert get_debug_flag() == expected_flag - assert get_debug_flag(default=True) == expected_default_flag + assert get_debug_flag() == expected_default_flag + + @pytest.mark.parametrize('env, ref_env, debug', [ + ('', 'production', False), + ('production', 'production', False), + ('development', 'development', True), + ('other', 'other', False), + ]) + def test_get_env(self, monkeypatch, env, ref_env, debug): + monkeypatch.setenv('FLASK_ENV', env) + assert get_debug_flag() == debug + assert get_env() == ref_env def test_make_response(self): app = flask.Flask(__name__) From 3738f7ff99661fa0319ebe2d8228ad8c479fb46a Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 10 Jan 2018 12:02:13 -0800 Subject: [PATCH 384/399] match run command behavior in app.run output extra server information handle env var changed by load_dotenv enable threading by default --- flask/app.py | 27 ++++++++++++++++---- flask/cli.py | 70 +++++++++++++++++++++++++++------------------------- 2 files changed, 58 insertions(+), 39 deletions(-) diff --git a/flask/app.py b/flask/app.py index e84e0a3f..1079bb27 100644 --- a/flask/app.py +++ b/flask/app.py @@ -864,21 +864,34 @@ class Flask(_PackageBoundObject): If installed, python-dotenv will be used to load environment variables from :file:`.env` and :file:`.flaskenv` files. - .. versionchanged:: 0.10 - The default port is now picked from the ``SERVER_NAME`` variable. + If set, the :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG` + environment variables will override :attr:`env` and + :attr:`debug`. + Threaded mode is enabled by default. + + .. versionchanged:: 0.10 + The default port is now picked from the ``SERVER_NAME`` + variable. """ # Change this into a no-op if the server is invoked from the - # command line. Have a look at cli.py for more information. + # command line. Have a look at cli.py for more information. if os.environ.get('FLASK_RUN_FROM_CLI') == 'true': from .debughelpers import explain_ignored_app_run explain_ignored_app_run() return if load_dotenv: - from flask.cli import load_dotenv - load_dotenv() + cli.load_dotenv() + # if set, let env vars override previous values + if 'FLASK_ENV' in os.environ: + self.env = get_env() + self.debug = get_debug_flag() + elif 'FLASK_DEBUG' in os.environ: + self.debug = get_debug_flag() + + # debug passed to method overrides all other sources if debug is not None: self.debug = bool(debug) @@ -892,8 +905,12 @@ class Flask(_PackageBoundObject): host = host or sn_host or _host port = int(port or sn_port or _port) + options.setdefault('use_reloader', self.debug) options.setdefault('use_debugger', self.debug) + options.setdefault('threaded', True) + + cli.show_server_banner(self.env, self.debug, self.name) from werkzeug.serving import run_simple diff --git a/flask/cli.py b/flask/cli.py index 2adadcc7..52a4f596 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -577,6 +577,28 @@ def load_dotenv(path=None): return new_dir is not None # at least one file was located and loaded +def show_server_banner(env, debug, app_import_path): + """Show extra startup messages the first time the server is run, + ignoring the reloader. + """ + if os.environ.get('WERKZEUG_RUN_MAIN') == 'true': + return + + if app_import_path is not None: + print(' * Serving Flask app "{0}"'.format(app_import_path)) + + print(' * Environment: {0}'.format(env)) + + if env == 'production': + click.secho( + ' WARNING: Do not use the development server in a production' + ' environment.', fg='red') + click.secho(' Use a production WSGI server instead.', dim=True) + + if debug is not None: + print(' * Debug mode: {0}'.format('on' if debug else 'off')) + + @click.command('run', short_help='Runs a development server.') @click.option('--host', '-h', default='127.0.0.1', help='The interface to bind to.') @@ -596,51 +618,31 @@ def load_dotenv(path=None): @pass_script_info def run_command(info, host, port, reload, debugger, eager_loading, with_threads): - """Runs a local development server for the Flask application. + """Run a local development server. - 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. + This server is for development purposes only. It does not provide + the stability, security, or performance of production WSGI servers. - The reloader and debugger are by default enabled if the debug flag of - Flask is enabled and disabled otherwise. + The reloader and debugger are enabled by default if + FLASK_ENV=development or FLASK_DEBUG=1. """ - from werkzeug.serving import run_simple - - if get_env() == 'production': - click.secho('Warning: Detected a production environment. Do not ' - 'use `flask run` for production use.', - fg='red') - click.secho('Use a production ready WSGI server instead', - dim=True) - debug = get_debug_flag() + if reload is None: - reload = bool(debug) + reload = debug + if debugger is None: - debugger = bool(debug) + debugger = debug + if eager_loading is None: eager_loading = not reload + show_server_banner(get_env(), debug, info.app_import_path) app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) - # 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': - # 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) - print(' * Env %s' % get_env()) - if debug is not None: - print(' * Forcing debug mode %s' % (debug and 'on' or 'off')) - - run_simple(host, port, app, use_reloader=reload, - use_debugger=debugger, threaded=with_threads) + from werkzeug.serving import run_simple + run_simple(host, port, app, use_reloader=reload, use_debugger=debugger, + threaded=with_threads) @click.command('shell', short_help='Runs a shell in the app context.') From 8bdf820e9b9c166883ef9717e127ba46ebe43cae Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 10 Jan 2018 13:53:45 -0800 Subject: [PATCH 385/399] reset standard os env after each test --- tests/conftest.py | 42 +++++++++++++++++++++++++++++++++++++++--- tests/test_cli.py | 19 ++++--------------- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 3a8ae69e..486d4b0a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,16 +6,52 @@ :copyright: (c) 2015 by the Flask Team, see AUTHORS for more details. :license: BSD, see LICENSE for more details. """ -import flask import gc import os -import sys import pkgutil -import pytest +import sys import textwrap + +import pytest +from _pytest import monkeypatch + +import flask from flask import Flask as _Flask +@pytest.fixture(scope='session', autouse=True) +def _standard_os_environ(): + """Set up ``os.environ`` at the start of the test session to have + standard values. Returns a list of operations that is used by + :func:`._reset_os_environ` after each test. + """ + mp = monkeypatch.MonkeyPatch() + out = ( + (os.environ, 'FLASK_APP', monkeypatch.notset), + (os.environ, 'FLASK_ENV', monkeypatch.notset), + (os.environ, 'FLASK_DEBUG', monkeypatch.notset), + (os.environ, 'FLASK_RUN_FROM_CLI', monkeypatch.notset), + (os.environ, 'WERKZEUG_RUN_MAIN', monkeypatch.notset), + ) + + for _, key, value in out: + if value is monkeypatch.notset: + mp.delenv(key, False) + else: + mp.setenv(key, value) + + yield out + mp.undo() + + +@pytest.fixture(autouse=True) +def _reset_os_environ(monkeypatch, _standard_os_environ): + """Reset ``os.environ`` to the standard environ after each test, + in case a test changed something without cleaning up. + """ + monkeypatch._setitem.extend(_standard_os_environ) + + class Flask(_Flask): testing = True secret_key = 'test key' diff --git a/tests/test_cli.py b/tests/test_cli.py index 811ef0c8..d9216f3d 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -23,9 +23,11 @@ from _pytest.monkeypatch import notset from click.testing import CliRunner from flask import Flask, current_app -from flask.cli import AppGroup, FlaskGroup, NoAppException, ScriptInfo, dotenv, \ - find_best_app, get_version, load_dotenv, locate_app, prepare_import, \ +from flask.cli import ( + AppGroup, FlaskGroup, NoAppException, ScriptInfo, dotenv, + find_best_app, get_version, load_dotenv, locate_app, prepare_import, with_appcontext +) cwd = os.getcwd() test_path = os.path.abspath(os.path.join( @@ -33,19 +35,6 @@ test_path = os.path.abspath(os.path.join( )) -@pytest.fixture(autouse=True) -def manage_os_environ(monkeypatch): - # can't use monkeypatch.delitem since we don't want to restore a value - os.environ.pop('FLASK_APP', None) - os.environ.pop('FLASK_DEBUG', None) - # use monkeypatch internals to force-delete environ keys - monkeypatch._setitem.extend(( - (os.environ, 'FLASK_APP', notset), - (os.environ, 'FLASK_DEBUG', notset), - (os.environ, 'FLASK_RUN_FROM_CLI', notset), - )) - - @pytest.fixture def runner(): return CliRunner() From 87c2e121e0bf32f5234eabbf4773a82f9d5523d2 Mon Sep 17 00:00:00 2001 From: David Lord Date: Wed, 10 Jan 2018 15:38:52 -0800 Subject: [PATCH 386/399] clean up FLASK_ENV docs [ci skip] --- docs/cli.rst | 35 +++++++++------- docs/config.rst | 84 +++++++++++++++++++++++++------------ docs/quickstart.rst | 11 ++--- docs/server.rst | 14 ++++--- docs/tutorial/packaging.rst | 4 +- flask/app.py | 43 ++++++++++--------- flask/helpers.py | 21 ++++++---- 7 files changed, 129 insertions(+), 83 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 55d09963..b82d5aff 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -116,37 +116,42 @@ context will be active, and the app instance will be imported. :: Use :meth:`~Flask.shell_context_processor` to add other automatic imports. + Environments ------------ .. versionadded:: 1.0 -The environment in which the Flask app should run is set by the -:envvar:`FLASK_ENV` environment variable. If not set it defaults to -``production``. The other default environment which is known is -``development``. If the env is set to ``development`` the debug mode is -for instance automatically enabled. +The environment in which the Flask app runs is set by the +:envvar:`FLASK_ENV` environment variable. If not set it defaults to +``production``. The other recognized environment is ``development``. +Flask and extensions may choose to enable behaviors based on the +environment. -Debug Mode ----------- - -Set the :envvar:`FLASK_DEBUG` environment variable to override the -application's :attr:`~Flask.debug` flag. The value ``1`` enables it, ``0`` -disables it. Forcing the debug flag on also enables the debugger and reloader -when running the development server. +If the env is set to ``development``, the ``flask`` command will enable +debug mode and ``flask run`` will enable the interactive debugger and +reloader. :: - $ FLASK_DEBUG=1 flask run + $ FLASK_ENV=development flask run * Serving Flask app "hello" - * Forcing debug mode on - * Env production + * Environment: development + * Debug mode: on * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with inotify reloader * Debugger is active! * Debugger PIN: 223-456-919 +Debug Mode +---------- + +Debug mode will be enabled when :envvar:`FLASK_ENV` is ``development``, +as described above. If you want to control debug mode separately, use +:envvar:`FLASK_DEBUG`. The value ``1`` enables it, ``0`` disables it. + + .. _dotenv: Environment Variables From dotenv diff --git a/docs/config.rst b/docs/config.rst index e8da82d1..777a1d28 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -20,6 +20,7 @@ object. This is the place where Flask itself puts certain configuration values and also where extensions can put their configuration values. But this is also where you can have your own configuration. + Configuration Basics -------------------- @@ -42,52 +43,77 @@ method:: SECRET_KEY=b'_5#y2L"F4Q8z\n\xec]/' ) + Environment and Debug Features ------------------------------ -Some values are special in that they can show unexpected behavior when -changed late. In particular that applies to the Flask environment and -debug mode. +The :data:`ENV` and :data:`DEBUG` config values are special because they +may behave inconsistently if changed after the app has begun setting up. +In order to set the environment and debug mode reliably, Flask uses +environment variables. -If you use the :command:`flask` script to start a local development server -for instance you should tell Flask that you want to work in the -development environment. For safety reasons we default the flask -environment to production mode instead of development. This is done -because development mode can turn on potentially unsafe features such as -the debugger by default. +The environment is used to indicate to Flask, extensions, and other +programs, like Sentry, what context Flask is running in. It is +controlled with the :envvar:`FLASK_ENV` environment variable and +defaults to ``production``. -To control the environment and such fundamental features Flask provides -the two environment variables :envvar:`FLASK_ENV` and :envvar:`FLASK_DEBUG`. -In versions of Flask older than 1.0 the :envvar:`FLASK_ENV` environment -variable did not exist. +Setting :envvar:`FLASK_ENV` to ``development`` will enable debug mode. +``flask run`` will use the interactive debugger and reloader by default +in debug mode. To control this separately from the environment, use the +:envvar:`FLASK_DEBUG` flag. + +.. versionchanged:: 1.0 + Added :envvar:`FLASK_ENV` to control the environment separately + from debug mode. The development environment enables debug mode. -The most common way to switch Flask to development mode is to tell it to -work on the ``development`` environment:: +To switch Flask to the development environment and enable debug mode, +set :envvar:`FLASK_ENV`:: -$ export FLASK_ENV=development -$ flask run + $ export FLASK_ENV=development + $ flask run -(On Windows you need to use ``set`` instead of ``export``). +(On Windows, use ``set`` instead of ``export``.) + +Using the environment variables as described above is recommended. While +it is possible to set :data:`ENV` and :data:`DEBUG` in your config or +code, this is strongly discouraged. They can't be read early by the +``flask`` command, and some systems or extensions may have already +configured themselves based on a previous value. -While you can attempt to flip the environment and debug flag separately in -the Flask config from the config file this is strongly discouraged as -those flags are often loaded early and changing them late might not apply -to all systems and extensions. Builtin Configuration Values ---------------------------- The following configuration values are used internally by Flask: +.. py:data:: ENV + + What environment the app is running in. Flask and extensions may + enable behaviors based on the environment, such as enabling debug + mode. The :attr:`~flask.Flask.env` attribute maps to this config + key. This is set by the :envvar:`FLASK_ENV` environment variable and + may not behave as expected if set in code. + + **Do not enable development when deploying in production.** + + Default: ``'production'`` + + .. versionadded:: 1.0 + .. py:data:: DEBUG - Enable debug mode. When using the development server with ``flask run`` or - ``app.run``, an interactive debugger will be shown for unhanlded - exceptions, and the server will be reloaded when code changes. + Whether debug mode is enabled. When using ``flask run`` to start the + development server, an interactive debugger will be shown for + unhandled exceptions, and the server will be reloaded when code + changes. The :attr:`~flask.Flask.debug` attribute maps to this + config key. This is enabled when :data:`ENV` is ``'development'`` + and is overridden by the ``FLASK_DEBUG`` environment variable. It + may not behave as expected if set in code. - **Do not enable debug mode in production.** + **Do not enable debug mode when deploying in production.** - Default: ``False`` + Default: ``True`` if :data:`ENV` is ``'production'``, or ``False`` + otherwise. .. py:data:: TESTING @@ -339,6 +365,10 @@ The following configuration values are used internally by Flask: ``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See :ref:`logging` for information about configuration. + Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment + variable. + + Configuring from Files ---------------------- diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 284f6d76..334d7dc4 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -130,16 +130,14 @@ 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. -To enable all development features (and to disable the debug mode) you can -export the ``FLASK_ENV`` environment variable and set it to -``development`` +To enable all development features (including debug mode) you can export +the ``FLASK_ENV`` environment variable and set it to ``development`` before running the server:: $ export FLASK_ENV=development $ flask run -(On Windows you need to use ``set`` instead of ``export`` and on Flask -versions older than 1.0 you need to export ``FLASK_DEBUG=1`` instead). +(On Windows you need to use ``set`` instead of ``export``.) This does the following things: @@ -147,6 +145,9 @@ This does the following things: 2. it activates the automatic reloader 3. it enables the debug mode on the Flask application. +You can also control debug mode separately from the environment by +exporting ``FLASK_DEBUG=1``. + There are more parameters that are explained in the :ref:`server` docs. .. admonition:: Attention diff --git a/docs/server.rst b/docs/server.rst index 7e03d8df..db431a6c 100644 --- a/docs/server.rst +++ b/docs/server.rst @@ -21,21 +21,23 @@ this:: $ export FLASK_ENV=development $ flask run -This will enable the debugger, the reloader and then start the server on +This enables the development environment, including the interactive +debugger and reloader, and then starts 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 +arguments to the ``run`` option. For instance the reloader can be disabled:: $ flask run --no-reload .. note:: - On older Flask version (before 1.0) the :envvar:`FLASK_ENV` - environment variable is not supported and you need to enable the - debug mode separately by setting the :envvar:`FLASK_DEBUG` environment - variable to ``1``. + Prior to Flask 1.0 the :envvar:`FLASK_ENV` environment variable was + not supported and you needed to enable debug mode by exporting + ``FLASK_DEBUG=1``. This can still be used to control debug mode, but + you should prefer setting the development environment as shown + above. In Code ------- diff --git a/docs/tutorial/packaging.rst b/docs/tutorial/packaging.rst index 13e11d5c..e08f26fa 100644 --- a/docs/tutorial/packaging.rst +++ b/docs/tutorial/packaging.rst @@ -82,8 +82,8 @@ Do this on Mac or Linux with the following commands in ``flaskr/``:: flask run (In case you are on Windows you need to use ``set`` instead of ``export``). -The :envvar:`FLASK_ENV` flag if set to ``development`` turns on all -development features such as enabling the interactive debugger. +Exporting ``FLASK_ENV=development`` turns on all development features +such as enabling the interactive debugger. *Never leave debug mode activated in a production system*, because it will allow users to execute code on the server! diff --git a/flask/app.py b/flask/app.py index 1079bb27..a31eac91 100644 --- a/flask/app.py +++ b/flask/app.py @@ -785,36 +785,39 @@ class Flask(_PackageBoundObject): rv.update(processor()) return rv - #: The environment value. This is typically set from outside the - #: process by setting the `FLASK_ENV` environment variable and can be - #: used to quickly switch between different environments like - #: `production` and `development`. If not set this defaults to - #: `production`. + #: What environment the app is running in. Flask and extensions may + #: enable behaviors based on the environment, such as enabling debug + #: mode. This maps to the :data:`ENV` config key. This is set by the + #: :envvar:`FLASK_ENV` environment variable and may not behave as + #: expected if set in code. + #: + #: **Do not enable development when deploying in production.** + #: + #: Default: ``'production'`` env = ConfigAttribute('ENV') def _get_debug(self): return self.config['DEBUG'] + def _set_debug(self, value): - self._set_debug_value(value) + self.config['DEBUG'] = value + self.jinja_env.auto_reload = self.templates_auto_reload - #: The debug flag. If this is ``True`` it enables debugging of the - #: application. In debug mode the debugger will kick in when an - #: unhandled exception occurs and the integrated server will - #: automatically reload the application if changes in the code are - #: detected. + #: Whether debug mode is enabled. When using ``flask run`` to start + #: the development server, an interactive debugger will be shown for + #: unhandled exceptions, and the server will be reloaded when code + #: changes. This maps to the :data:`DEBUG` config key. This is + #: enabled when :attr:`env` is ``'development'`` and is overridden + #: by the ``FLASK_DEBUG`` environment variable. It may not behave as + #: expected if set in code. #: - #: This value should only be configured by the :envvar:`FLASK_DEBUG` - #: environment variable. Changing it by other means will not yield - #: consistent results. The default value depends on the Flask - #: environment and will be true for the development environment and false - #: otherwise. + #: **Do not enable debug mode when deploying in production.** + #: + #: Default: ``True`` if :attr:`env` is ``'development'``, or + #: ``False`` otherwise. debug = property(_get_debug, _set_debug) del _get_debug, _set_debug - def _set_debug_value(self, value): - self.config['DEBUG'] = value - self.jinja_env.auto_reload = self.templates_auto_reload - def run(self, host=None, port=None, debug=None, load_dotenv=True, **options): """Runs the application on a local development server. diff --git a/flask/helpers.py b/flask/helpers.py index 705ea3e1..922509cf 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -47,19 +47,24 @@ _os_alt_seps = list(sep for sep in [os.path.sep, os.path.altsep] def get_env(): - val = os.environ.get('FLASK_ENV') - if not val: - val = 'production' - return val + """Get the environment the app is running in, indicated by the + :envvar:`FLASK_ENV` environment variable. The default is + ``'production'``. + """ + return os.environ.get('FLASK_ENV') or 'production' def get_debug_flag(): + """Get whether debug mode should be enabled for the app, indicated + by the :envvar:`FLASK_DEBUG` environment variable. The default is + ``True`` if :func:`.get_env` returns ``'development'``, or ``False`` + otherwise. + """ val = os.environ.get('FLASK_DEBUG') + if not val: - env = get_env() - if env == 'development': - return True - return False + return get_env() == 'development' + return val.lower() not in ('0', 'false', 'no') From a9646716d25c1705cfa33561d41482ba0e1c06d4 Mon Sep 17 00:00:00 2001 From: Nickatak Date: Sun, 14 Jan 2018 16:02:43 -0800 Subject: [PATCH 387/399] Fix typo in example. --- 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 d0215c58..c7ae14f9 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -65,7 +65,7 @@ the file and redirects the user to the URL for the uploaded file:: if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) - return redirect(url_for('uploaded_file', + return redirect(url_for('upload_file', filename=filename)) return ''' From a1d9ebe4abebdb824aa9a397b001de5f9130e7a3 Mon Sep 17 00:00:00 2001 From: Fadhel_Chaabane Date: Tue, 23 Jan 2018 13:57:50 +0000 Subject: [PATCH 388/399] New Feature: Added Support for cookie's SameSite attribute. --- docs/config.rst | 9 ++++++++- docs/security.rst | 6 ++++-- flask/app.py | 1 + flask/sessions.py | 11 ++++++++++- tests/test_basic.py | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 4 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 777a1d28..c1854c8e 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -208,6 +208,14 @@ The following configuration values are used internally by Flask: Default: ``False`` +.. py:data:: SESSION_COOKIE_SAMESITE + + Browser will only send cookies to the domain that created them. + There are two possible values for the same-site attribute: "Strict" and "Lax" + If set to "None", the samesite flag is not set. + + Default: ``None`` + .. py:data:: PERMANENT_SESSION_LIFETIME If ``session.permanent`` is true, the cookie's expiration will be set this @@ -635,4 +643,3 @@ Example usage for both:: # or via open_instance_resource: with app.open_instance_resource('application.cfg') as f: config = f.read() - diff --git a/docs/security.rst b/docs/security.rst index 13ea2e33..b68e909e 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -195,16 +195,18 @@ They can be set on other cookies too. - ``HttpOnly`` protects the contents of cookies from being read with JavaScript. - ``SameSite`` ensures that cookies can only be requested from the same - domain that created them. It is not supported by Flask yet. + domain that created them. There are two possible values for the same-site + attribute: "Strict" and "Lax" :: app.config.update( SESSION_COOKIE_SECURE=True, SESSION_COOKIE_HTTPONLY=True, + SESSION_COOKIE_SAMESITE='Strict' ) - response.set_cookie('username', 'flask', secure=True, httponly=True) + response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Strict') Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after the given time, or the current time plus the age, respectively. If neither diff --git a/flask/app.py b/flask/app.py index a31eac91..200b5c20 100644 --- a/flask/app.py +++ b/flask/app.py @@ -284,6 +284,7 @@ class Flask(_PackageBoundObject): 'SESSION_COOKIE_PATH': None, 'SESSION_COOKIE_HTTPONLY': True, 'SESSION_COOKIE_SECURE': False, + 'SESSION_COOKIE_SAMESITE': None, 'SESSION_REFRESH_EACH_REQUEST': True, 'MAX_CONTENT_LENGTH': None, 'SEND_FILE_MAX_AGE_DEFAULT': timedelta(hours=12), diff --git a/flask/sessions.py b/flask/sessions.py index 8f111718..eb028027 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -249,6 +249,13 @@ class SessionInterface(object): """ return app.config['SESSION_COOKIE_SECURE'] + def get_cookie_samesite(self, app): + """Returns "Strict", "Lax" or None if the cookie should use + samesite attribute. This currently just returns the value of + the ``SESSION_COOKIE_SAMESITE`` setting. + """ + return app.config['SESSION_COOKIE_SAMESITE'] + def get_expiration_time(self, app, session): """A helper method that returns an expiration date for the session or ``None`` if the session is linked to the browser session. The @@ -362,6 +369,7 @@ class SecureCookieSessionInterface(SessionInterface): httponly = self.get_cookie_httponly(app) secure = self.get_cookie_secure(app) + samesite = self.get_cookie_samesite(app) expires = self.get_expiration_time(app, session) val = self.get_signing_serializer(app).dumps(dict(session)) response.set_cookie( @@ -371,5 +379,6 @@ class SecureCookieSessionInterface(SessionInterface): httponly=httponly, domain=domain, path=path, - secure=secure + secure=secure, + samesite=samesite ) diff --git a/tests/test_basic.py b/tests/test_basic.py index d7406baf..b0397ee6 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -319,6 +319,7 @@ def test_session_using_session_settings(app, client): SESSION_COOKIE_DOMAIN='.example.com', SESSION_COOKIE_HTTPONLY=False, SESSION_COOKIE_SECURE=True, + SESSION_COOKIE_SAMESITE='Strict', SESSION_COOKIE_PATH='/' ) @@ -333,8 +334,45 @@ def test_session_using_session_settings(app, client): assert 'path=/' in cookie assert 'secure' in cookie assert 'httponly' not in cookie + assert 'samesite' in cookie +def test_session_using_samesite_attribute(app, client): + app.config.update( + SERVER_NAME='www.example.com:8080', + APPLICATION_ROOT='/test', + SESSION_COOKIE_DOMAIN='.example.com', + SESSION_COOKIE_HTTPONLY=False, + SESSION_COOKIE_SECURE=True, + SESSION_COOKIE_SAMESITE='anyvalue', + SESSION_COOKIE_PATH='/' + ) + + @app.route('/') + def index(): + flask.session['testing'] = 42 + return 'Hello World' + + # assert excption when samesite is not set to 'Strict', 'Lax' or None + with pytest.raises(ValueError): + rv = client.get('/', 'http://www.example.com:8080/test/') + + # assert the samesite flag is not set in the cookie, when set to None + app.config.update(SESSION_COOKIE_SAMESITE=None) + rv = client.get('/', 'http://www.example.com:8080/test/') + cookie = rv.headers['set-cookie'].lower() + assert 'samesite' not in cookie + + app.config.update(SESSION_COOKIE_SAMESITE='Strict') + rv = client.get('/', 'http://www.example.com:8080/test/') + cookie = rv.headers['set-cookie'].lower() + assert 'samesite=strict' in cookie + + app.config.update(SESSION_COOKIE_SAMESITE='Lax') + rv = client.get('/', 'http://www.example.com:8080/test/') + cookie = rv.headers['set-cookie'].lower() + assert 'samesite=lax' in cookie + def test_session_localhost_warning(recwarn, app, client): app.config.update( SERVER_NAME='localhost:5000', From db5735c3ceaa04e9d9d05d942686bd6e369d0f34 Mon Sep 17 00:00:00 2001 From: Fadhel_Chaabane Date: Tue, 23 Jan 2018 15:02:07 +0000 Subject: [PATCH 389/399] Changed Werkzeug min version to 0.14 to support SameSite cookie's attribute --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 4bfaaed1..a88f5fff 100644 --- a/tox.ini +++ b/tox.ini @@ -16,7 +16,7 @@ deps = blinker python-dotenv - lowest: Werkzeug==0.9 + lowest: Werkzeug==0.14 lowest: Jinja2==2.4 lowest: itsdangerous==0.21 lowest: Click==4.0 From 2beedabaafb16f2ba255c54c0875beb0d43664fd Mon Sep 17 00:00:00 2001 From: David Lord Date: Mon, 22 Jan 2018 12:16:37 -0800 Subject: [PATCH 390/399] add HTTPS support for flask run command --- CHANGES | 3 ++ flask/cli.py | 99 ++++++++++++++++++++++++++++++++++++++++++++--- tests/test_cli.py | 65 ++++++++++++++++++++++++++++++- 3 files changed, 159 insertions(+), 8 deletions(-) diff --git a/CHANGES b/CHANGES index 3517f1a7..cf9f6de4 100644 --- a/CHANGES +++ b/CHANGES @@ -118,6 +118,8 @@ Major release, unreleased - The dev server now uses threads by default. (`#2529`_) - Loading config files with ``silent=True`` will ignore ``ENOTDIR`` errors. (`#2581`_) +- Pass ``--cert`` and ``--key`` options to ``flask run`` to run the + development server over HTTPS. (`#2606`_) .. _pallets/meta#24: https://github.com/pallets/meta/issues/24 .. _#1421: https://github.com/pallets/flask/issues/1421 @@ -154,6 +156,7 @@ Major release, unreleased .. _#2450: https://github.com/pallets/flask/pull/2450 .. _#2529: https://github.com/pallets/flask/pull/2529 .. _#2581: https://github.com/pallets/flask/pull/2581 +.. _#2606: https://github.com/pallets/flask/pull/2606 Version 0.12.3 diff --git a/flask/cli.py b/flask/cli.py index 52a4f596..43e6aa5a 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -14,6 +14,7 @@ import ast import inspect import os import re +import ssl import sys import traceback from functools import update_wrapper @@ -21,9 +22,10 @@ from operator import attrgetter from threading import Lock, Thread import click +from werkzeug.utils import import_string from . import __version__ -from ._compat import getargspec, iteritems, reraise +from ._compat import getargspec, iteritems, reraise, text_type from .globals import current_app from .helpers import get_debug_flag, get_env @@ -599,25 +601,110 @@ def show_server_banner(env, debug, app_import_path): print(' * Debug mode: {0}'.format('on' if debug else 'off')) +class CertParamType(click.ParamType): + """Click option type for the ``--cert`` option. Allows either an + existing file, the string ``'adhoc'``, or an import for a + :class:`~ssl.SSLContext` object. + """ + + name = 'path' + + def __init__(self): + self.path_type = click.Path( + exists=True, dir_okay=False, resolve_path=True) + + def convert(self, value, param, ctx): + try: + return self.path_type(value, param, ctx) + except click.BadParameter: + value = click.STRING(value, param, ctx).lower() + + if value == 'adhoc': + try: + import OpenSSL + except ImportError: + raise click.BadParameter( + 'Using ad-hoc certificates requires pyOpenSSL.', + ctx, param) + + return value + + obj = import_string(value, silent=True) + + if sys.version_info < (2, 7): + if obj: + return obj + else: + if isinstance(obj, ssl.SSLContext): + return obj + + raise + + +def _validate_key(ctx, param, value): + """The ``--key`` option must be specified when ``--cert`` is a file. + Modifies the ``cert`` param to be a ``(cert, key)`` pair if needed. + """ + cert = ctx.params.get('cert') + is_adhoc = cert == 'adhoc' + + if sys.version_info < (2, 7): + is_context = cert and not isinstance(cert, (text_type, bytes)) + else: + is_context = isinstance(cert, ssl.SSLContext) + + if value is not None: + if is_adhoc: + raise click.BadParameter( + 'When "--cert" is "adhoc", "--key" is not used.', + ctx, param) + + if is_context: + raise click.BadParameter( + 'When "--cert" is an SSLContext object, "--key is not used.', + ctx, param) + + if not cert: + raise click.BadParameter( + '"--cert" must also be specified.', + ctx, param) + + ctx.params['cert'] = cert, value + + else: + if cert and not (is_adhoc or is_context): + raise click.BadParameter( + 'Required when using "--cert".', + ctx, param) + + return value + + @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, help='The port to bind to.') +@click.option('--cert', type=CertParamType(), + help='Specify a certificate file to use HTTPS.') +@click.option('--key', + type=click.Path(exists=True, dir_okay=False, resolve_path=True), + callback=_validate_key, expose_value=False, + help='The key file to use when specifying a certificate.') @click.option('--reload/--no-reload', default=None, - help='Enable or disable the reloader. By default the reloader ' + help='Enable or disable the reloader. By default the reloader ' 'is active if debug is enabled.') @click.option('--debugger/--no-debugger', default=None, - help='Enable or disable the debugger. By default the debugger ' + 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 ' + help='Enable or disable eager loading. By default eager ' 'loading is enabled if the reloader is disabled.') @click.option('--with-threads/--without-threads', default=True, help='Enable or disable multithreading.') @pass_script_info def run_command(info, host, port, reload, debugger, eager_loading, - with_threads): + with_threads, cert): """Run a local development server. This server is for development purposes only. It does not provide @@ -642,7 +729,7 @@ def run_command(info, host, port, reload, debugger, eager_loading, from werkzeug.serving import run_simple run_simple(host, port, app, use_reloader=reload, use_debugger=debugger, - threaded=with_threads) + threaded=with_threads, ssl_context=cert) @click.command('shell', short_help='Runs a shell in the app context.') diff --git a/tests/test_cli.py b/tests/test_cli.py index d9216f3d..f1e5eba7 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -14,7 +14,9 @@ from __future__ import absolute_import import os +import ssl import sys +import types from functools import partial import click @@ -24,8 +26,8 @@ from click.testing import CliRunner from flask import Flask, current_app from flask.cli import ( - AppGroup, FlaskGroup, NoAppException, ScriptInfo, dotenv, - find_best_app, get_version, load_dotenv, locate_app, prepare_import, + AppGroup, FlaskGroup, NoAppException, ScriptInfo, dotenv, find_best_app, + get_version, load_dotenv, locate_app, prepare_import, run_command, with_appcontext ) @@ -464,3 +466,62 @@ def test_dotenv_optional(monkeypatch): monkeypatch.chdir(test_path) load_dotenv() assert 'FOO' not in os.environ + + +def test_run_cert_path(): + # no key + with pytest.raises(click.BadParameter): + run_command.make_context('run', ['--cert', __file__]) + + # no cert + with pytest.raises(click.BadParameter): + run_command.make_context('run', ['--key', __file__]) + + ctx = run_command.make_context( + 'run', ['--cert', __file__, '--key', __file__]) + assert ctx.params['cert'] == (__file__, __file__) + + +def test_run_cert_adhoc(monkeypatch): + monkeypatch.setitem(sys.modules, 'OpenSSL', None) + + # pyOpenSSL not installed + with pytest.raises(click.BadParameter): + run_command.make_context('run', ['--cert', 'adhoc']) + + # pyOpenSSL installed + monkeypatch.setitem(sys.modules, 'OpenSSL', types.ModuleType('OpenSSL')) + ctx = run_command.make_context('run', ['--cert', 'adhoc']) + assert ctx.params['cert'] == 'adhoc' + + # no key with adhoc + with pytest.raises(click.BadParameter): + run_command.make_context('run', ['--cert', 'adhoc', '--key', __file__]) + + +def test_run_cert_import(monkeypatch): + monkeypatch.setitem(sys.modules, 'not_here', None) + + # ImportError + with pytest.raises(click.BadParameter): + run_command.make_context('run', ['--cert', 'not_here']) + + # not an SSLContext + if sys.version_info >= (2, 7): + with pytest.raises(click.BadParameter): + run_command.make_context('run', ['--cert', 'flask']) + + # SSLContext + if sys.version_info < (2, 7): + ssl_context = object() + else: + ssl_context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + + monkeypatch.setitem(sys.modules, 'ssl_context', ssl_context) + ctx = run_command.make_context('run', ['--cert', 'ssl_context']) + assert ctx.params['cert'] is ssl_context + + # no --key with SSLContext + with pytest.raises(click.BadParameter): + run_command.make_context( + 'run', ['--cert', 'ssl_context', '--key', __file__]) From 382b13581ed44e0bc740968353bb5a25945bafdf Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 23 Jan 2018 15:11:50 -0800 Subject: [PATCH 391/399] clean up samesite docs --- docs/config.rst | 12 ++++++++---- docs/security.rst | 18 +++++++++++++----- flask/sessions.py | 6 +++--- tests/test_basic.py | 25 ++++++++----------------- 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index c1854c8e..2e2833f9 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -210,12 +210,14 @@ The following configuration values are used internally by Flask: .. py:data:: SESSION_COOKIE_SAMESITE - Browser will only send cookies to the domain that created them. - There are two possible values for the same-site attribute: "Strict" and "Lax" - If set to "None", the samesite flag is not set. + Restrict how cookies are sent with requests from external sites. Can + be set to ``'Lax'`` (recommended) or ``'Strict'``. + See :ref:`security-cookie`. Default: ``None`` + .. versionadded:: 1.0 + .. py:data:: PERMANENT_SESSION_LIFETIME If ``session.permanent`` is true, the cookie's expiration will be set this @@ -369,13 +371,15 @@ The following configuration values are used internally by Flask: ``LOGGER_HANDLER_POLICY``, ``EXPLAIN_TEMPLATE_LOADING`` .. versionchanged:: 1.0 - ``LOGGER_NAME`` and ``LOGGER_HANDLER_POLICY`` were removed. See :ref:`logging` for information about configuration. Added :data:`ENV` to reflect the :envvar:`FLASK_ENV` environment variable. + Added :data:`SESSION_COOKIE_SAMESITE` to control the session + cookie's ``SameSite`` option. + Configuring from Files ---------------------- diff --git a/docs/security.rst b/docs/security.rst index b68e909e..44c095ac 100644 --- a/docs/security.rst +++ b/docs/security.rst @@ -184,6 +184,9 @@ contains the same data. :: - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection + +.. _security-cookie: + Set-Cookie options ~~~~~~~~~~~~~~~~~~ @@ -194,19 +197,21 @@ They can be set on other cookies too. - ``Secure`` limits cookies to HTTPS traffic only. - ``HttpOnly`` protects the contents of cookies from being read with JavaScript. -- ``SameSite`` ensures that cookies can only be requested from the same - domain that created them. There are two possible values for the same-site - attribute: "Strict" and "Lax" +- ``SameSite`` restricts how cookies are sent with requests from + external sites. Can be set to ``'Lax'`` (recommended) or ``'Strict'``. + ``Lax`` prevents sending cookies with CSRF-prone requests from + external sites, such as submitting a form. ``Strict`` prevents sending + cookies with all external requests, including following regular links. :: app.config.update( SESSION_COOKIE_SECURE=True, SESSION_COOKIE_HTTPONLY=True, - SESSION_COOKIE_SAMESITE='Strict' + SESSION_COOKIE_SAMESITE='Lax', ) - response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Strict') + response.set_cookie('username', 'flask', secure=True, httponly=True, samesite='Lax') Specifying ``Expires`` or ``Max-Age`` options, will remove the cookie after the given time, or the current time plus the age, respectively. If neither @@ -239,6 +244,9 @@ values (or any values that need secure signatures). - https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies - https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie +.. _samesite_support: https://caniuse.com/#feat=same-site-cookie-attribute + + HTTP Public Key Pinning (HPKP) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/flask/sessions.py b/flask/sessions.py index eb028027..621f3f5e 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -250,9 +250,9 @@ class SessionInterface(object): return app.config['SESSION_COOKIE_SECURE'] def get_cookie_samesite(self, app): - """Returns "Strict", "Lax" or None if the cookie should use - samesite attribute. This currently just returns the value of - the ``SESSION_COOKIE_SAMESITE`` setting. + """Return ``'Strict'`` or ``'Lax'`` if the cookie should use the + ``SameSite`` attribute. This currently just returns the value of + the :data:`SESSION_COOKIE_SAMESITE` setting. """ return app.config['SESSION_COOKIE_SAMESITE'] diff --git a/tests/test_basic.py b/tests/test_basic.py index b0397ee6..0e55b52e 100644 --- a/tests/test_basic.py +++ b/tests/test_basic.py @@ -319,7 +319,7 @@ def test_session_using_session_settings(app, client): SESSION_COOKIE_DOMAIN='.example.com', SESSION_COOKIE_HTTPONLY=False, SESSION_COOKIE_SECURE=True, - SESSION_COOKIE_SAMESITE='Strict', + SESSION_COOKIE_SAMESITE='Lax', SESSION_COOKIE_PATH='/' ) @@ -338,41 +338,32 @@ def test_session_using_session_settings(app, client): def test_session_using_samesite_attribute(app, client): - app.config.update( - SERVER_NAME='www.example.com:8080', - APPLICATION_ROOT='/test', - SESSION_COOKIE_DOMAIN='.example.com', - SESSION_COOKIE_HTTPONLY=False, - SESSION_COOKIE_SECURE=True, - SESSION_COOKIE_SAMESITE='anyvalue', - SESSION_COOKIE_PATH='/' - ) - @app.route('/') def index(): flask.session['testing'] = 42 return 'Hello World' - # assert excption when samesite is not set to 'Strict', 'Lax' or None + app.config.update(SESSION_COOKIE_SAMESITE='invalid') + with pytest.raises(ValueError): - rv = client.get('/', 'http://www.example.com:8080/test/') + client.get('/') - # assert the samesite flag is not set in the cookie, when set to None app.config.update(SESSION_COOKIE_SAMESITE=None) - rv = client.get('/', 'http://www.example.com:8080/test/') + rv = client.get('/') cookie = rv.headers['set-cookie'].lower() assert 'samesite' not in cookie app.config.update(SESSION_COOKIE_SAMESITE='Strict') - rv = client.get('/', 'http://www.example.com:8080/test/') + rv = client.get('/') cookie = rv.headers['set-cookie'].lower() assert 'samesite=strict' in cookie app.config.update(SESSION_COOKIE_SAMESITE='Lax') - rv = client.get('/', 'http://www.example.com:8080/test/') + rv = client.get('/') cookie = rv.headers['set-cookie'].lower() assert 'samesite=lax' in cookie + def test_session_localhost_warning(recwarn, app, client): app.config.update( SERVER_NAME='localhost:5000', From 0570393bc471d72eca128ded5edc868f13a87b0a Mon Sep 17 00:00:00 2001 From: Grey Li Date: Mon, 22 Jan 2018 11:07:37 +0800 Subject: [PATCH 392/399] Update cli help for FLASK_ENV --- flask/cli.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 52a4f596..76ca125d 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -731,12 +731,12 @@ A general utility script for Flask applications. Provides commands from Flask, extensions, and the application. Loads the application defined in the FLASK_APP environment variable, or from a wsgi.py -file. Debug mode can be controlled with the FLASK_DEBUG -environment variable. +file. Setting the FLASK_ENV environment variable to 'development' will enable +debug mode. \b {prefix}{cmd} FLASK_APP=hello.py - {prefix}{cmd} FLASK_DEBUG=1 + {prefix}{cmd} FLASK_ENV=development {prefix}flask run """.format( cmd='export' if os.name == 'posix' else 'set', From 213afec771a92e20516e5ea468bdd2186915f186 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 23 Jan 2018 15:34:52 -0800 Subject: [PATCH 393/399] add rst extension for changelog fix changelog version --- CHANGES => CHANGES.rst | 25 +++++++++++-------------- docs/changelog.rst | 2 +- scripts/make-release.py | 2 +- 3 files changed, 13 insertions(+), 16 deletions(-) rename CHANGES => CHANGES.rst (98%) diff --git a/CHANGES b/CHANGES.rst similarity index 98% rename from CHANGES rename to CHANGES.rst index cf9f6de4..88d585db 100644 --- a/CHANGES +++ b/CHANGES.rst @@ -1,16 +1,15 @@ Flask Changelog =============== -Here you can see the full list of changes between each Flask release. +Version 1.0 +----------- -Version 0.13 ------------- - -Major release, unreleased +unreleased - **Python 2.6 and 3.3 are no longer supported.** (`pallets/meta#24`_) -- Minimum Werkzeug version bumped to 0.9, but please use the latest version. +- Minimum Werkzeug version bumped to 0.14, but please use the latest + version. - Minimum Click version bumped to 4, but please use the latest version. - Make ``app.run()`` into a noop if a Flask application is run from the development server on the command line. This avoids some behavior that @@ -115,11 +114,15 @@ Major release, unreleased depending on ``app.debug``. No handlers are removed, and a handler is only added if no handlers are already configured. (`#2436`_) - Blueprint view function name may not contain dots. (`#2450`_) +- Fix a ``ValueError`` caused by invalid Range requests in some cases. + (`#2526`_) - The dev server now uses threads by default. (`#2529`_) - Loading config files with ``silent=True`` will ignore ``ENOTDIR`` errors. (`#2581`_) - Pass ``--cert`` and ``--key`` options to ``flask run`` to run the development server over HTTPS. (`#2606`_) +- Added :data:`SESSION_COOKIE_SAMESITE` to control the ``SameSite`` + attribute on the session cookie. (`#2607`_) .. _pallets/meta#24: https://github.com/pallets/meta/issues/24 .. _#1421: https://github.com/pallets/flask/issues/1421 @@ -154,17 +157,11 @@ Major release, unreleased .. _#2430: https://github.com/pallets/flask/pull/2430 .. _#2436: https://github.com/pallets/flask/pull/2436 .. _#2450: https://github.com/pallets/flask/pull/2450 +.. _#2526: https://github.com/pallets/flask/issues/2526 .. _#2529: https://github.com/pallets/flask/pull/2529 .. _#2581: https://github.com/pallets/flask/pull/2581 .. _#2606: https://github.com/pallets/flask/pull/2606 - - -Version 0.12.3 --------------- - -Bugfix release, unreleased - -- Fix a ValueError caused by invalid Range requests in some cases +.. _#2607: https://github.com/pallets/flask/pull/2607 Version 0.12.2 diff --git a/docs/changelog.rst b/docs/changelog.rst index d6c5f48c..d9e113ec 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -1 +1 @@ -.. include:: ../CHANGES +.. include:: ../CHANGES.rst diff --git a/scripts/make-release.py b/scripts/make-release.py index fc6421ab..7fb42787 100644 --- a/scripts/make-release.py +++ b/scripts/make-release.py @@ -21,7 +21,7 @@ _date_clean_re = re.compile(r'(\d+)(st|nd|rd|th)') def parse_changelog(): - with open('CHANGES') as f: + with open('CHANGES.rst') as f: lineiter = iter(f) for line in lineiter: match = re.search('^Version\s+(.*)', line.strip()) From 8a1f3626c5b21191fd02cc4eb72cb3ff62d240e3 Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 23 Jan 2018 15:37:33 -0800 Subject: [PATCH 394/399] fix changelog list formatting --- CHANGES.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index 88d585db..b352dc42 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -87,17 +87,17 @@ unreleased if ``app.jinja_env`` was already accessed. (`#2373`_) - The following old deprecated code was removed. (`#2385`_) - - ``flask.ext`` - import extensions directly by their name instead of - through the ``flask.ext`` namespace. For example, - ``import flask.ext.sqlalchemy`` becomes ``import flask_sqlalchemy``. - - ``Flask.init_jinja_globals`` - extend ``Flask.create_jinja_environment`` - instead. - - ``Flask.error_handlers`` - tracked by ``Flask.error_handler_spec``, - use ``@app.errorhandler`` to register handlers. - - ``Flask.request_globals_class`` - use ``Flask.app_ctx_globals_class`` - instead. - - ``Flask.static_path`` - use ``Flask.static_url_path`` instead. - - ``Request.module`` - use ``Request.blueprint`` instead. + - ``flask.ext`` - import extensions directly by their name instead of + through the ``flask.ext`` namespace. For example, + ``import flask.ext.sqlalchemy`` becomes ``import flask_sqlalchemy``. + - ``Flask.init_jinja_globals`` - extend ``Flask.create_jinja_environment`` + instead. + - ``Flask.error_handlers`` - tracked by ``Flask.error_handler_spec``, + use ``@app.errorhandler`` to register handlers. + - ``Flask.request_globals_class`` - use ``Flask.app_ctx_globals_class`` + instead. + - ``Flask.static_path`` - use ``Flask.static_url_path`` instead. + - ``Request.module`` - use ``Request.blueprint`` instead. - The ``request.json`` property is no longer deprecated. (`#1421`_) - Support passing an existing ``EnvironBuilder`` or ``dict`` to From 2cba0d77eef983b605bf2f5c85a9d02fe212eda0 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 28 Jan 2018 11:20:42 -0800 Subject: [PATCH 395/399] use traceback to detect call more reliably --- flask/cli.py | 37 +++++++++++++++++++++++++++---------- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 0a556ddc..383fe459 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -33,14 +33,6 @@ except ImportError: dotenv = None -def _called_with_wrong_args(factory, exc_info): - exc_type, exc_value, tb = exc_info - return exc_type is TypeError and \ - str(exc_value).startswith(( - '%s() takes' % factory.__name__, - '%s() missing' % factory.__name__)) - - class NoAppException(click.UsageError): """Raised if an application cannot be found or loaded.""" @@ -83,7 +75,7 @@ def find_best_app(script_info, module): if isinstance(app, Flask): return app except TypeError: - if not _called_with_wrong_args(app_factory, sys.exc_info()): + if not _called_with_wrong_args(app_factory): raise raise NoAppException( 'Detected factory "{factory}" in module "{module}", but ' @@ -121,6 +113,30 @@ def call_factory(script_info, app_factory, arguments=()): return app_factory() +def _called_with_wrong_args(factory): + """Check whether calling a function raised a ``TypeError`` because + the call failed or because something in the factory raised the + error. + + :param factory: the factory function that was called + :return: true if the call failed + """ + tb = sys.exc_info()[2] + + try: + while tb is not None: + if tb.tb_frame.f_code is factory.__code__: + # in the factory, it was called successfully + return False + + tb = tb.tb_next + + # didn't reach the factory + return True + finally: + del tb + + def find_app_by_string(script_info, module, app_name): """Checks if the given string is a variable name or a function. If it is a function, it checks for specified arguments and whether it takes a @@ -158,8 +174,9 @@ def find_app_by_string(script_info, module, app_name): try: app = call_factory(script_info, attr, args) except TypeError as e: - if not _called_with_wrong_args(attr, sys.exc_info()): + if not _called_with_wrong_args(attr): raise + raise NoAppException( '{e}\nThe factory "{app_name}" in module "{module}" could not ' 'be called with the specified arguments.'.format( From 8bddd3bf4da669717acea4fa71cdccc433090570 Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 28 Jan 2018 12:06:59 -0800 Subject: [PATCH 396/399] allow nightly to fail on travis --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index 440bdf95..e156d1b3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,9 @@ matrix: env: TOXENV=py,codecov - python: nightly env: TOXENV=py + allow_failures: + - python: nightly + env: TOXENV=py install: - pip install tox From 7fb2ddcaf613438820a4c095ba9c972e41fc154b Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 28 Jan 2018 14:25:48 -0800 Subject: [PATCH 397/399] add note about syntax errors in custom scripts mention pycharm version support --- docs/cli.rst | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index b82d5aff..e378a91a 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -355,15 +355,25 @@ script is available. Note that you don't need to set ``FLASK_APP``. :: $ pip install -e . $ wiki run +.. admonition:: Errors in Custom Scripts + + When using a custom script, if you introduce an error in your + module-level code, the reloader will fail because it can no longer + load the entry point. + + The ``flask`` command, being separate from your code, does not have + this issue and is recommended in most cases. + .. _console script: https://packaging.python.org/tutorials/distributing-packages/#console-scripts PyCharm Integration ------------------- -The Flask CLI features aren't yet fully integrated into the PyCharm IDE, -so we have to do a few tweaks to get them working smoothly. These -instructions should be similar for any other IDE you might want to use. +Prior to PyCharm 2018.1, the Flask CLI features weren't yet fully +integrated into PyCharm. We have to do a few tweaks to get them working +smoothly. These instructions should be similar for any other IDE you +might want to use. In PyCharm, with your project open, click on *Run* from the menu bar and go to *Edit Configurations*. You'll be greeted by a screen similar to From ae30fe7de00e2afa1835062a26f2c2f3f4e42e4b Mon Sep 17 00:00:00 2001 From: David Lord Date: Sun, 28 Jan 2018 18:48:39 -0800 Subject: [PATCH 398/399] document testing cli commands --- docs/cli.rst | 3 +++ docs/testing.rst | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/docs/cli.rst b/docs/cli.rst index e378a91a..456fdc03 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -247,6 +247,9 @@ group. This is useful if you want to organize multiple related commands. :: flask user create demo +See :ref:`testing-cli` for an overview of how to test your custom +commands. + Application Context ~~~~~~~~~~~~~~~~~~~ diff --git a/docs/testing.rst b/docs/testing.rst index a040b7ef..79856341 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -406,3 +406,50 @@ Passing the ``json`` argument in the test client methods sets the request data to the JSON-serialized object and sets the content type to ``application/json``. You can get the JSON data from the request or response with ``get_json``. + + +.. _testing-cli: + +Testing CLI Commands +-------------------- + +Click comes with `utilities for testing`_ your CLI commands. + +Use :meth:`CliRunner.invoke ` to call +commands in the same way they would be called from the command line. The +:class:`~click.testing.CliRunner` runs the command in isolation and +captures the output in a :class:`~click.testing.Result` object. :: + + import click + from click.testing import CliRunner + + @app.cli.command('hello') + @click.option('--name', default='World') + def hello_command(name) + click.echo(f'Hello, {name}!') + + def test_hello(): + runner = CliRunner() + result = runner.invoke(hello_command, ['--name', 'Flask']) + assert 'Hello, Flask' in result.output + +If you want to test how your command parses parameters, without running +the command, use the command's :meth:`~click.BaseCommand.make_context` +method. This is useful for testing complex validation rules and custom +types. :: + + def upper(ctx, param, value): + if value is not None: + return value.upper() + + @app.cli.command('hello') + @click.option('--name', default='World', callback=upper) + def hello_command(name) + click.echo(f'Hello, {name}!') + + def test_hello_params(): + context = hello_command.make_context('hello', ['--name', 'flask']) + assert context.params['name'] == 'FLASK' + +.. _click: http://click.pocoo.org/ +.. _utilities for testing: http://click.pocoo.org/testing From af32ea0b9449a2f4aeb858f546cdfea7254342fb Mon Sep 17 00:00:00 2001 From: David Lord Date: Tue, 6 Feb 2018 08:03:09 -0800 Subject: [PATCH 399/399] bump dependency versions --- CHANGES.rst | 7 ++++--- flask/helpers.py | 19 +++++++------------ setup.py | 29 +++++++++++++++-------------- tox.ini | 6 +++--- 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/CHANGES.rst b/CHANGES.rst index b352dc42..b1b65a45 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -8,9 +8,9 @@ Version 1.0 unreleased - **Python 2.6 and 3.3 are no longer supported.** (`pallets/meta#24`_) -- Minimum Werkzeug version bumped to 0.14, but please use the latest - version. -- Minimum Click version bumped to 4, but please use the latest version. +- Bump minimum dependency versions to the latest stable versions: + Werkzeug >= 0.14, Jinja >= 2.10, itsdangerous >= 0.24, Click >= 5.1. + (`#2586`_) - Make ``app.run()`` into a noop if a Flask application is run from the development server on the command line. This avoids some behavior that was confusing to debug for newcomers. @@ -159,6 +159,7 @@ unreleased .. _#2450: https://github.com/pallets/flask/pull/2450 .. _#2526: https://github.com/pallets/flask/issues/2526 .. _#2529: https://github.com/pallets/flask/pull/2529 +.. _#2586: https://github.com/pallets/flask/issues/2586 .. _#2581: https://github.com/pallets/flask/pull/2581 .. _#2606: https://github.com/pallets/flask/pull/2606 .. _#2607: https://github.com/pallets/flask/pull/2607 diff --git a/flask/helpers.py b/flask/helpers.py index 922509cf..49968bfc 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -608,18 +608,13 @@ def send_file(filename_or_fp, mimetype=None, as_attachment=False, 'headers' % filename, stacklevel=2) if conditional: - if callable(getattr(Range, 'to_content_range_header', None)): - # Werkzeug supports Range Requests - # Remove this test when support for Werkzeug <0.12 is dropped - try: - rv = rv.make_conditional(request, accept_ranges=True, - complete_length=fsize) - except RequestedRangeNotSatisfiable: - if file is not None: - file.close() - raise - else: - rv = rv.make_conditional(request) + try: + rv = rv.make_conditional(request, accept_ranges=True, + complete_length=fsize) + except RequestedRangeNotSatisfiable: + if file is not None: + file.close() + raise # make sure we don't send x-sendfile for servers that # ignore the 304 status code for x-sendfile. if rv.status_code == 304: diff --git a/setup.py b/setup.py index bf2d0bc6..2ece939a 100644 --- a/setup.py +++ b/setup.py @@ -68,28 +68,26 @@ setup( include_package_data=True, zip_safe=False, platforms='any', + python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', install_requires=[ - 'Werkzeug>=0.9', - 'Jinja2>=2.4', - 'itsdangerous>=0.21', - 'click>=4.0', + 'Werkzeug>=0.14', + 'Jinja2>=2.10', + 'itsdangerous>=0.24', + 'click>=5.1', ], extras_require={ 'dotenv': ['python-dotenv'], 'dev': [ - 'blinker', - 'python-dotenv', - 'greenlet', 'pytest>=3', 'coverage', 'tox', 'sphinx', - 'sphinxcontrib-log-cabinet' ], }, classifiers=[ - 'Development Status :: 4 - Beta', + 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', + 'Framework :: Flask', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', @@ -101,10 +99,13 @@ setup( 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', - 'Topic :: Software Development :: Libraries :: Python Modules' + 'Topic :: Internet :: WWW/HTTP :: WSGI :: Application', + 'Topic :: Software Development :: Libraries :: Application Frameworks', + 'Topic :: Software Development :: Libraries :: Python Modules', ], - entry_points=''' - [console_scripts] - flask=flask.cli:main - ''' + entry_points={ + 'console_scripts': [ + 'flask = flask.cli:main', + ], + }, ) diff --git a/tox.ini b/tox.ini index a88f5fff..d74c8758 100644 --- a/tox.ini +++ b/tox.ini @@ -17,9 +17,9 @@ deps = python-dotenv lowest: Werkzeug==0.14 - lowest: Jinja2==2.4 - lowest: itsdangerous==0.21 - lowest: Click==4.0 + lowest: Jinja2==2.10 + lowest: itsdangerous==0.24 + lowest: Click==5.1 devel: https://github.com/pallets/werkzeug/archive/master.tar.gz devel: https://github.com/pallets/markupsafe/archive/master.tar.gz

    W$eQ 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 Df&34i diff --git a/docs/cli.rst b/docs/cli.rst index cdb0fc59..1a6da62f 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -359,11 +359,10 @@ There's quite a few options to change, but once we've done it for one command, we can easily copy the entire configuration and make a single tweak to give us access to other commands, including any custom ones you may implement yourself. -For the *Script* input (**A**), navigate to your project's virtual environment. -Within that folder, pick the ``flask`` executable which will reside in the -``bin`` folder, or in the ``Scripts`` on Windows. +Since PyCharm 2017.3, you can run modules instead of needing to find ``flask`` +executable. For the *Module name* input (**A**), you just need to input ``flask``. -The *Script Parameter* field (**B**) is set to the CLI command you to execute. +The *Parameters* field (**B**) is set to the CLI command you to execute. In this example we use ``run``, which will run the development server. You can skip this next step if you're using :ref:`dotenv`. We need to add an From 5892a6f2c5aefeb54598a824c9775b487999ba90 Mon Sep 17 00:00:00 2001 From: Ngalim Siregar Date: Sun, 3 Dec 2017 17:05:20 +0900 Subject: [PATCH 361/399] Update PyCharm image add highlighted area to pycharm image --- docs/_static/pycharm-runconfig.png | Bin 55441 -> 55565 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/_static/pycharm-runconfig.png b/docs/_static/pycharm-runconfig.png index 7e2de581bba4c30f08c005bcb8c2b4ef8036d91f..abfccd7af173cd71ccc3a772227e0b81bcd35ec9 100644 GIT binary patch literal 55565 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 literal 55441 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%0A})?rud6tdC`q#Q=j0K!TNH?ehs&-!6VA5B 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 z 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 z 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 Yp&8@b3dw28KPF(R# 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ÐNKC!{nB=)@lkWiHYwoId6sR_vl!q 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 Date: Wed, 30 Aug 2017 14:52:57 -0700 Subject: [PATCH 336/399] Update App Engine link Point to an authoritative site for Flask on App Engine that is maintained. --- docs/deploying/index.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deploying/index.rst b/docs/deploying/index.rst index da8ac14e..6f014bff 100644 --- a/docs/deploying/index.rst +++ b/docs/deploying/index.rst @@ -20,7 +20,7 @@ Hosted options - `Deploying Flask on Heroku `_ - `Deploying Flask on OpenShift `_ - `Deploying Flask on Webfaction `_ -- `Deploying Flask on Google App Engine `_ +- `Deploying Flask on Google App Engine `_ - `Deploying Flask on AWS Elastic Beanstalk `_ - `Sharing your Localhost Server with Localtunnel `_ - `Deploying on Azure (IIS) `_ From 62a6aabc8db72f2e606529d6e9b2146596b0d515 Mon Sep 17 00:00:00 2001 From: Jaap Broekhuizen Date: Wed, 6 Sep 2017 13:30:05 +0200 Subject: [PATCH 337/399] Mention Flask instead of Jinja in the issue template For people not really comfortable with the flask ecosystem this can be really confusing. --- .github/ISSUE_TEMPLATE.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE.rst b/.github/ISSUE_TEMPLATE.rst index 7c85c7fc..8b6910fb 100644 --- a/.github/ISSUE_TEMPLATE.rst +++ b/.github/ISSUE_TEMPLATE.rst @@ -1,6 +1,6 @@ **This issue tracker is a tool to address bugs in Flask itself. Please use the #pocoo IRC channel on freenode or Stack Overflow for general -questions about using Jinja or issues not related to Jinja.** +questions about using Flask or issues not related to Flask.** If you'd like to report a bug in Flask, fill out the template below. Provide any any extra information that may be useful / related to your problem. From ff8f525fb30ccaf1a3af6f6694c1d6385221731c Mon Sep 17 00:00:00 2001 From: ImgBotApp Date: Tue, 12 Sep 2017 04:24:35 +0000 Subject: [PATCH 338/399] [ImgBot] optimizes images \docs\_static\no.png (18.3%)) \docs\_static\touch-icon.png (64.21%)) \docs\_static\flaskr.png (8.32%)) \docs\_static\pycharm-runconfig.png (14.65%)) \docs\_static\debugger.png (2.79%)) \docs\_static\flask.png (30.15%)) \docs\_static\yes.png (13%)) \docs\_static\logo-full.png (29.33%)) --- docs/_static/debugger.png | Bin 213851 -> 207889 bytes docs/_static/flask.png | Bin 9925 -> 6933 bytes docs/_static/flaskr.png | Bin 72263 -> 66253 bytes docs/_static/logo-full.png | Bin 20806 -> 14703 bytes docs/_static/no.png | Bin 317 -> 259 bytes docs/_static/pycharm-runconfig.png | Bin 162651 -> 138820 bytes docs/_static/touch-icon.png | Bin 3624 -> 1297 bytes docs/_static/yes.png | Bin 277 -> 241 bytes 8 files changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/_static/debugger.png b/docs/_static/debugger.png index 558a9114596d49b0639e7871b7e71f14c18fc89e..7d4181f6a41dfe2ab698c18834c8d64f5ccb1a8f 100644 GIT binary patch delta 155 zcmccJ#yjx_&je+r28PYs#9116_?UucFfcSQc)B=-EZ#M}fhSz(#dMg!@?GML4Lpej z!4nW7(}5xyFs0tSrP+fN^7dhgF8*LKE_fkAr$$cF8^ d#F@LbMcG%>Ra(`rwKih_0#8>zmvv4FO#uCNG#~%~ delta 6154 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 diff --git a/docs/_static/flask.png b/docs/_static/flask.png index 5c603cc22c086a9257ee1c91277c42413e6650c4..55cb8478ef215c3735a12cf5e4c902b967c0054f 100644 GIT binary patch literal 6933 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 diff --git a/docs/_static/flaskr.png b/docs/_static/flaskr.png index a1552b52df96791004ecab06354df4a64a918737..838f760472249e1bd480a42c405b815e9cbae633 100644 GIT binary patch delta 87 zcmX@Uh2?B3%LHYn28PYs#9116PTzXX!@$tM;OXKRvU%6^FYJu@o7d0huHdP)e)yb$ pfvE;0G5w+>qyKhJ2}TVjQGVr|vlAvBZvDgn1fH&bF6*2UngExR9~l4u delta 6140 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%0Dd$UE{PkI)(G(#rf#+$; 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 literal 20806 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 QOaK4?07*qoM6N<$f(R}^rT_o{ diff --git a/docs/_static/no.png b/docs/_static/no.png index 4ac1083d91123945ba3ff2f545fa1c8efd00d688..644c3f70f95f2cb336cbb6b2ffcbbf33ad64184b 100644 GIT binary patch delta 242 zcmV3K~yNuebGHqgisI!;7{ZNq#NQ9@CbMW zH?j=bBft@sBe+L^Py?%$bTdp1xdN%70sj`|L^IRR|QasB0gUB(5@r(4@Skw}z^?KdY;`Ph9e%WwuWlzRErvJkmUq#SN)w9>_qz?-wq;|LE@)3w!+ s!qXeSaefwW^{L9a%BKPWN%_+AW3auXJt}l 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 Df&34i literal 162651 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