From 1e39871d681bd41451cf8694cd67fffe017fa522 Mon Sep 17 00:00:00 2001 From: Quentin Roy Date: Sat, 20 Oct 2012 18:18:42 +0200 Subject: [PATCH 001/731] Add back path=None argument into find_module. This fixes #618 and revert #524. --- scripts/flaskext_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/flaskext_compat.py b/scripts/flaskext_compat.py index 050fd7e0..cb0b436c 100644 --- a/scripts/flaskext_compat.py +++ b/scripts/flaskext_compat.py @@ -44,7 +44,7 @@ class ExtensionImporter(object): def install(self): sys.meta_path[:] = [x for x in sys.meta_path if self != x] + [self] - def find_module(self, fullname): + def find_module(self, fullname, path=None): if fullname.startswith(self.prefix): return self From 3d67736e090f55fcdc476452ac89a79fdd14cc23 Mon Sep 17 00:00:00 2001 From: Daniel Richman Date: Sat, 17 Aug 2013 22:40:06 +0000 Subject: [PATCH 002/731] Check error handlers for specific classes first This allows adding error handlers like this: @app.errorhandler(werkzeug.exceptions.Forbidden) And subclassing HTTPExceptions: class ForbiddenBecauseReason(Forbidden): pass @app.errorhandler(ForbiddenBecauseReason) def error1(): return "Forbidden because reason", 403 @app.errorhandler(403) def error2(): return "Forbidden", 403 ... the idea being, that a flask extension might want to raise an exception, with the default behaviour of creating a HTTP error page, but still allowing the user to add a view/handler specific to that exception (e.g., "Forbidden because you are not in the right group"). --- CHANGES | 6 ++++++ flask/app.py | 5 +++-- flask/testsuite/basic.py | 41 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 8699718c..dcedf67c 100644 --- a/CHANGES +++ b/CHANGES @@ -14,6 +14,12 @@ Version 1.0 `False` it will only be modified if the session actually modifies. Non permanent sessions are not affected by this and will always expire if the browser window closes. +- Error handlers that match specific classes are now checked first, + thereby allowing catching exceptions that are subclasses of HTTP + exceptions (in ``werkzeug.execptions``). This makes it possible + for an extension author to create exceptions that will by default + result in the HTTP error of their choosing, but may be caught with + a custom error handler if desired. Version 0.10.2 -------------- diff --git a/flask/app.py b/flask/app.py index 805dc166..c97e8b3c 100644 --- a/flask/app.py +++ b/flask/app.py @@ -1365,8 +1365,6 @@ class Flask(_PackageBoundObject): # wants the traceback preserved in handle_http_exception. Of course # we cannot prevent users from trashing it themselves in a custom # trap_http_exception method so that's their fault then. - if isinstance(e, HTTPException) and not self.trap_http_exception(e): - return self.handle_http_exception(e) blueprint_handlers = () handlers = self.error_handler_spec.get(request.blueprint) @@ -1377,6 +1375,9 @@ class Flask(_PackageBoundObject): if isinstance(e, typecheck): return handler(e) + if isinstance(e, HTTPException) and not self.trap_http_exception(e): + return self.handle_http_exception(e) + reraise(exc_type, exc_value, tb) def handle_exception(self, e): diff --git a/flask/testsuite/basic.py b/flask/testsuite/basic.py index 71a1f832..8c13bda7 100644 --- a/flask/testsuite/basic.py +++ b/flask/testsuite/basic.py @@ -18,7 +18,7 @@ from datetime import datetime from threading import Thread from flask.testsuite import FlaskTestCase, emits_module_deprecation_warning from flask._compat import text_type -from werkzeug.exceptions import BadRequest, NotFound +from werkzeug.exceptions import BadRequest, NotFound, Forbidden from werkzeug.http import parse_date from werkzeug.routing import BuildError @@ -626,12 +626,18 @@ class BasicFunctionalityTestCase(FlaskTestCase): @app.errorhandler(500) def internal_server_error(e): return 'internal server error', 500 + @app.errorhandler(Forbidden) + def forbidden(e): + return 'forbidden', 403 @app.route('/') def index(): flask.abort(404) @app.route('/error') def error(): 1 // 0 + @app.route('/forbidden') + def error2(): + flask.abort(403) c = app.test_client() rv = c.get('/') self.assert_equal(rv.status_code, 404) @@ -639,6 +645,9 @@ class BasicFunctionalityTestCase(FlaskTestCase): rv = c.get('/error') self.assert_equal(rv.status_code, 500) self.assert_equal(b'internal server error', rv.data) + rv = c.get('/forbidden') + self.assert_equal(rv.status_code, 403) + self.assert_equal(b'forbidden', rv.data) def test_before_request_and_routing_errors(self): app = flask.Flask(__name__) @@ -668,6 +677,36 @@ class BasicFunctionalityTestCase(FlaskTestCase): c = app.test_client() self.assert_equal(c.get('/').data, b'42') + def test_http_error_subclass_handling(self): + class ForbiddenSubclass(Forbidden): + pass + + app = flask.Flask(__name__) + @app.errorhandler(ForbiddenSubclass) + def handle_forbidden_subclass(e): + self.assert_true(isinstance(e, ForbiddenSubclass)) + return 'banana' + @app.errorhandler(403) + def handle_forbidden_subclass(e): + self.assert_false(isinstance(e, ForbiddenSubclass)) + self.assert_true(isinstance(e, Forbidden)) + return 'apple' + + @app.route('/1') + def index1(): + raise ForbiddenSubclass() + @app.route('/2') + def index2(): + flask.abort(403) + @app.route('/3') + def index3(): + raise Forbidden() + + c = app.test_client() + self.assert_equal(c.get('/1').data, b'banana') + self.assert_equal(c.get('/2').data, b'apple') + self.assert_equal(c.get('/3').data, b'apple') + def test_trapping_of_bad_request_key_errors(self): app = flask.Flask(__name__) app.testing = True From db1be12aea8193487e7f3097d03ea81780525069 Mon Sep 17 00:00:00 2001 From: Andrew Plummer Date: Wed, 26 Mar 2014 16:40:25 +0000 Subject: [PATCH 003/731] Fix import in viewdecorators.rst for @templated --- docs/patterns/viewdecorators.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/viewdecorators.rst b/docs/patterns/viewdecorators.rst index a0948577..46bab334 100644 --- a/docs/patterns/viewdecorators.rst +++ b/docs/patterns/viewdecorators.rst @@ -127,7 +127,7 @@ way you can still use the redirect function or return simple strings. Here the code for that decorator:: from functools import wraps - from flask import request + from flask import request, render_template def templated(template=None): def decorator(f): From b2b531a36bab939f76df51b7ae88f87e60eea8f6 Mon Sep 17 00:00:00 2001 From: Andrew Plummer Date: Wed, 26 Mar 2014 16:52:06 +0000 Subject: [PATCH 004/731] Tweak wording in viewdecorators.rst --- docs/patterns/viewdecorators.rst | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/docs/patterns/viewdecorators.rst b/docs/patterns/viewdecorators.rst index 46bab334..0e523cfd 100644 --- a/docs/patterns/viewdecorators.rst +++ b/docs/patterns/viewdecorators.rst @@ -2,12 +2,12 @@ View Decorators =============== Python has a really interesting feature called function decorators. This -allow some really neat things for web applications. Because each view in -Flask is a function decorators can be used to inject additional +allows some really neat things for web applications. Because each view in +Flask is a function, decorators can be used to inject additional functionality to one or more functions. The :meth:`~flask.Flask.route` decorator is the one you probably used already. But there are use cases for implementing your own decorator. For instance, imagine you have a -view that should only be used by people that are logged in to. If a user +view that should only be used by people that are logged in. If a user goes to the site and is not logged in, they should be redirected to the login page. This is a good example of a use case where a decorator is an excellent solution. @@ -54,7 +54,7 @@ because of that you would like to cache the generated results for a certain amount of time. A decorator would be nice for that. We're assuming you have set up a cache like mentioned in :ref:`caching-pattern`. -Here an example cache function. It generates the cache key from a +Here is an example cache function. It generates the cache key from a specific prefix (actually a format string) and the current path of the request. Notice that we are using a function that first creates the decorator that then decorates the function. Sounds awful? Unfortunately @@ -124,7 +124,7 @@ the dictionary returned is passed to the template rendering function. If a dictionary is returned we return it from the function unchanged. That way you can still use the redirect function or return simple strings. -Here the code for that decorator:: +Here is the code for that decorator:: from functools import wraps from flask import request, render_template @@ -163,6 +163,3 @@ to a view function. This is possible with this decorator. For example:: @app.endpoint('index') def my_index(): return "Hello world" - - - From 42c28c3758fc1612a5a1e89a36092580344c4578 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 15:58:27 +0200 Subject: [PATCH 005/731] Fixed intention in flask init --- flask/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/flask/__init__.py b/flask/__init__.py index bf4774dc..f1560879 100644 --- a/flask/__init__.py +++ b/flask/__init__.py @@ -21,8 +21,8 @@ from jinja2 import Markup, escape from .app import Flask, Request, Response from .config import Config from .helpers import url_for, flash, send_file, send_from_directory, \ - get_flashed_messages, get_template_attribute, make_response, safe_join, \ - stream_with_context + get_flashed_messages, get_template_attribute, make_response, safe_join, \ + stream_with_context from .globals import current_app, g, request, session, _request_ctx_stack, \ _app_ctx_stack from .ctx import has_request_context, has_app_context, \ From 7503bde2235c28853d4c4e53087090d8c42041aa Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 16:34:17 +0200 Subject: [PATCH 006/731] Added the new flask run module to start the server. --- flask/run.py | 220 +++++++++++++++++++++++++++++++++++++++++++++++++++ setup.py | 4 + 2 files changed, 224 insertions(+) create mode 100644 flask/run.py diff --git a/flask/run.py b/flask/run.py new file mode 100644 index 00000000..cbf3c909 --- /dev/null +++ b/flask/run.py @@ -0,0 +1,220 @@ +import os +import sys +from threading import Lock +from optparse import OptionParser + +from werkzeug.serving import run_simple + +from ._compat import iteritems + + +def find_best_app(module): + """Given a module instance this tries to find the best possible application + in the module or raises a RuntimeError. + """ + from flask import Flask + + # The app name wins, even if it's not a flask object. + app = getattr(module, 'app', None) + if app is not None and callable(app): + return app + + # Otherwise find the first object named Flask + matches = [] + for key, value in iteritems(module.__dict__): + if isinstance(value, Flask): + matches.append(value) + + if matches: + if len(matches) > 1: + raise RuntimeError('More than one possible Flask application ' + 'found in module "%s", none of which are called ' + '"app". Be explicit!' % module) + return matches[0] + + raise RuntimeError('Failed to find application in module "%s". Are ' + 'you sure it contains a Flask application? Maybe ' + 'you wrapped it in a WSGI middleware or you are ' + 'using a factory function.' % module) + + +def prepare_exec_for_file(filename): + module = [] + + # Chop off file extensions or package markers + if filename.endswith('.py'): + filename = filename[:-3] + elif os.path.split(filename)[1] == '__init__.py': + filename = os.path.dirname(filename) + filename = os.path.realpath(filename) + + dirpath = filename + while 1: + dirpath, extra = os.path.split(dirpath) + module.append(extra) + if not os.path.isfile(os.path.join(dirpath, '__init__.py')): + break + + sys.path.insert(0, dirpath) + return '.'.join(module[::-1]) + + +def locate_app(app_id, debug=None): + """Attempts to locate the application.""" + if ':' in app_id: + module, app_obj = app_id.split(':', 1) + else: + module = app_id + app_obj = None + + __import__(module) + mod = sys.modules[module] + if app_obj is None: + app = find_best_app(mod) + else: + app = getattr(mod, app_obj, None) + if app is None: + raise RuntimeError('Failed to find application in module "%s"' + % module) + if debug is not None: + app.debug = debug + return app + + +class DispatchingApp(object): + """Special applicationt that dispatches to a flask application which + is imported by name on first request. This is safer than importing + the application upfront because it means that we can forward all + errors for import problems into the browser as error. + """ + + def __init__(self, app_id, debug=None, use_eager_loading=False): + self.app_id = app_id + self.app = None + self.debug = debug + self._lock = Lock() + if use_eager_loading: + self._load_unlocked() + + def _load_unlocked(self): + self.app = rv = locate_app(self.app_id, self.debug) + return rv + + def __call__(self, environ, start_response): + if self.app is not None: + return self.app(environ, start_response) + with self._lock: + if self.app is not None: + rv = self.app + else: + rv = self._load_unlocked() + return rv(environ, start_response) + + +def run_application(app_id, host='127.0.0.1', port=5000, debug=None, + use_reloader=False, use_debugger=False, + use_eager_loading=None, magic_app_id=True): + """Useful function to start a Werkzeug server for an application that + is known by it's import name. By default the app ID can also be a + full file name in which case Flask attempts to reconstruct the import + name from it and do the right thing. + + :param app_id: the import name of the application module. If a colon + is provided, everything afterwards is the application + object name. In case the magic app id is enabled this + can also be a filename. + :param host: the host to bind to. + :param port: the port to bind to. + :param debug: if set to something other than None then the application's + debug flag will be set to this. + :param use_reloader: enables or disables the reloader. + :param use_debugger: enables or disables the builtin debugger. + :param use_eager_loading: enables or disables eager loading. This is + normally conditional to the reloader. + :param magic_app_id: if this is enabled then the app id can also be a + filename instead of an import module and Flask + will attempt to reconstruct the import name. + """ + if magic_app_id: + if os.path.isfile(app_id) or os.sep in app_id or \ + os.altsep is not None and os.altsep in app_id: + app_id = prepare_exec_for_file(app_id) + + if use_eager_loading is None: + use_eager_loading = not use_reloader + + # Extra startup messages. This depends a but on Werkzeug internals to + # not double execute when the reloader kicks in. + if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': + print ' * Serving Flask app "%s"' % app_id + if debug is not None: + print ' * Forcing debug %s' % (debug and 'on' or 'off') + + app = DispatchingApp(app_id, debug, use_eager_loading) + run_simple(host, port, app, use_reloader=use_reloader, + use_debugger=use_debugger) + + +def main(as_module=False): + this_module = __package__ + '.run' + + if as_module: + name = 'python -m ' + this_module + else: + name = 'flask-run' + + parser = OptionParser(usage='%prog [options] module', prog=name) + parser.add_option('--debug', action='store_true', + dest='debug', help='Flip debug flag on. If enabled ' + 'this also affects debugger and reloader defaults.') + parser.add_option('--no-debug', action='store_false', + dest='debug', help='Flip debug flag off.') + parser.add_option('--host', default='127.0.0.1', + help='The host to bind on. (defaults to 127.0.0.1)') + parser.add_option('--port', default=5000, + help='The port to bind on. (defaults to 5000)') + parser.add_option('--with-reloader', action='store_true', + dest='with_reloader', + help='Enable the reloader.') + parser.add_option('--without-reloader', action='store_false', + dest='with_reloader', + help='Disable the reloader.') + parser.add_option('--with-debugger', action='store_true', + dest='with_debugger', + help='Enable the debugger.') + parser.add_option('--without-debugger', action='store_false', + dest='with_debugger', + help='Disable the debugger.') + parser.add_option('--with-eager-loading', action='store_true', + dest='with_eager_loading', + help='Force enable the eager-loading. This makes the ' + 'application load immediately but makes development ' + 'flows harder. It\'s not recommended to enable eager ' + 'loading when the reloader is enabled as it can lead ' + 'to unexpected crashes.') + parser.add_option('--without-eager-loading', action='store_false', + dest='with_eager_loading', + help='Disable the eager-loading.') + opts, args = parser.parse_args() + if len(args) != 1: + parser.error('Expected exactly one argument which is the import ' + 'name of the application.') + + if opts.with_debugger is None: + opts.with_debugger = opts.debug + if opts.with_reloader is None: + opts.with_reloader = opts.debug + + # This module is always executed as "python -m flask.run" and as such + # we need to ensure that we restore the actual command line so that + # the reloader can properly operate. + sys.argv = ['-m', this_module] + sys.argv[1:] + + run_application(args[0], opts.host, opts.port, debug=opts.debug, + use_reloader=opts.with_reloader, + use_debugger=opts.with_debugger, + use_eager_loading=opts.with_eager_loading) + + +if __name__ == '__main__': + main(as_module=True) diff --git a/setup.py b/setup.py index 5ddfe246..0fbf7e8d 100644 --- a/setup.py +++ b/setup.py @@ -106,6 +106,10 @@ setup( 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 'Topic :: Software Development :: Libraries :: Python Modules' ], + entry_points=''' + [console_scripts] + flask-run=flask.run:main + ''', cmdclass={'audit': run_audit}, test_suite='flask.testsuite.suite' ) From e46bca4051e51f3812d4a0b25f60f7b7959668c2 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 16:39:22 +0200 Subject: [PATCH 007/731] Added thread flag to flask run --- CHANGES | 3 +++ flask/run.py | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 8fc7670b..80c4b27d 100644 --- a/CHANGES +++ b/CHANGES @@ -26,6 +26,9 @@ Version 1.0 - Added a workaround for a limitation in Python 3.3's namespace loader. - Added support for explicit root paths when using Python 3.3's namespace packages. +- Added ``flask-run`` and the ``flask.run`` module to start the local + debug server. This is recommended over the old ``flask.run()`` method + as it works faster and more reliable due to a different design. Version 0.10.2 -------------- diff --git a/flask/run.py b/flask/run.py index cbf3c909..89344616 100644 --- a/flask/run.py +++ b/flask/run.py @@ -113,7 +113,8 @@ class DispatchingApp(object): def run_application(app_id, host='127.0.0.1', port=5000, debug=None, use_reloader=False, use_debugger=False, - use_eager_loading=None, magic_app_id=True): + use_eager_loading=None, magic_app_id=True, + **options): """Useful function to start a Werkzeug server for an application that is known by it's import name. By default the app ID can also be a full file name in which case Flask attempts to reconstruct the import @@ -134,6 +135,10 @@ def run_application(app_id, host='127.0.0.1', port=5000, debug=None, :param magic_app_id: if this is enabled then the app id can also be a filename instead of an import module and Flask will attempt to reconstruct the import name. + :param options: the options to be forwarded to the underlying + Werkzeug server. See + :func:`werkzeug.serving.run_simple` for more + information. """ if magic_app_id: if os.path.isfile(app_id) or os.sep in app_id or \ @@ -152,7 +157,7 @@ def run_application(app_id, host='127.0.0.1', port=5000, debug=None, app = DispatchingApp(app_id, debug, use_eager_loading) run_simple(host, port, app, use_reloader=use_reloader, - use_debugger=use_debugger) + use_debugger=use_debugger, **options) def main(as_module=False): @@ -195,6 +200,13 @@ def main(as_module=False): parser.add_option('--without-eager-loading', action='store_false', dest='with_eager_loading', help='Disable the eager-loading.') + parser.add_option('--with-threads', action='store_true', + dest='with_threads', + help='Enable multi-threading to handle multiple ' + 'requests concurrently.') + parser.add_option('--without-threads', action='store_false', + dest='with_threads', + help='Disables multi-threading. (default)') opts, args = parser.parse_args() if len(args) != 1: parser.error('Expected exactly one argument which is the import ' @@ -213,7 +225,8 @@ def main(as_module=False): run_application(args[0], opts.host, opts.port, debug=opts.debug, use_reloader=opts.with_reloader, use_debugger=opts.with_debugger, - use_eager_loading=opts.with_eager_loading) + use_eager_loading=opts.with_eager_loading, + threaded=opts.with_threads) if __name__ == '__main__': From 8a46eec4f89ccd93d1fab231a5cbccbae0352e57 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 17:51:52 +0200 Subject: [PATCH 008/731] Fixed a reference in the docs. --- docs/patterns/errorpages.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/patterns/errorpages.rst b/docs/patterns/errorpages.rst index ddf73c93..79f94a48 100644 --- a/docs/patterns/errorpages.rst +++ b/docs/patterns/errorpages.rst @@ -1,3 +1,5 @@ +.. _errorpages: + Custom Error Pages ================== From 1b06c8a41119f8c93078e85f6474cb85d7e30b43 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 21 Apr 2014 17:52:04 +0200 Subject: [PATCH 009/731] Added __main__ module --- flask/__main__.py | 15 +++++++++++++++ flask/run.py | 16 +++++++++++++++- 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 flask/__main__.py diff --git a/flask/__main__.py b/flask/__main__.py new file mode 100644 index 00000000..e3c3bd34 --- /dev/null +++ b/flask/__main__.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +""" + flask.__main__ + ~~~~~~~~~~~~~~ + + Alias for flask.run for the command line. + + :copyright: (c) 2014 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + + +if __name__ == '__main__': + from run import main + main(as_module=True) diff --git a/flask/run.py b/flask/run.py index 89344616..9d86df02 100644 --- a/flask/run.py +++ b/flask/run.py @@ -1,3 +1,14 @@ +# -*- coding: utf-8 -*- +""" + flask.run + ~~~~~~~~~ + + A simple command line application to run flask apps. + + :copyright: (c) 2014 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + import os import sys from threading import Lock @@ -164,7 +175,10 @@ def main(as_module=False): this_module = __package__ + '.run' if as_module: - name = 'python -m ' + this_module + if sys.version_info >= (2, 7): + name = 'python -m ' + this_module.rsplit('.', 1)[0] + else: + name = 'python -m ' + this_module else: name = 'flask-run' From e21afed0bba82668c6e4f5f433d0ccfcf54b3577 Mon Sep 17 00:00:00 2001 From: Zak Johnson Date: Mon, 21 Apr 2014 15:37:23 -0700 Subject: [PATCH 010/731] Fix typos in docstrings and comments in run.py --- flask/run.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flask/run.py b/flask/run.py index 9d86df02..d16b3e0a 100644 --- a/flask/run.py +++ b/flask/run.py @@ -30,7 +30,7 @@ def find_best_app(module): if app is not None and callable(app): return app - # Otherwise find the first object named Flask + # Otherwise find exactly one Flask instance, or fail. matches = [] for key, value in iteritems(module.__dict__): if isinstance(value, Flask): @@ -52,7 +52,7 @@ def find_best_app(module): def prepare_exec_for_file(filename): module = [] - # Chop off file extensions or package markers + # Chop off file extensions or package markers. if filename.endswith('.py'): filename = filename[:-3] elif os.path.split(filename)[1] == '__init__.py': @@ -93,10 +93,10 @@ def locate_app(app_id, debug=None): class DispatchingApp(object): - """Special applicationt that dispatches to a flask application which + """Special application that dispatches to a flask application which is imported by name on first request. This is safer than importing - the application upfront because it means that we can forward all - errors for import problems into the browser as error. + the application up front because it means that we can forward all + errors for import problems into the browser as errors. """ def __init__(self, app_id, debug=None, use_eager_loading=False): @@ -127,7 +127,7 @@ def run_application(app_id, host='127.0.0.1', port=5000, debug=None, use_eager_loading=None, magic_app_id=True, **options): """Useful function to start a Werkzeug server for an application that - is known by it's import name. By default the app ID can also be a + is known by its import name. By default the app ID can also be a full file name in which case Flask attempts to reconstruct the import name from it and do the right thing. @@ -159,7 +159,7 @@ def run_application(app_id, host='127.0.0.1', port=5000, debug=None, if use_eager_loading is None: use_eager_loading = not use_reloader - # Extra startup messages. This depends a but on Werkzeug internals to + # Extra startup messages. This depends a bit on Werkzeug internals to # not double execute when the reloader kicks in. if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': print ' * Serving Flask app "%s"' % app_id From 52fa195d459a70f8ef73e954b20a9512b2f5001b Mon Sep 17 00:00:00 2001 From: "Carlos E. Garcia" Date: Wed, 23 Apr 2014 10:46:38 -0400 Subject: [PATCH 011/731] few mispelling errors --- docs/tutorial/dbcon.rst | 2 +- flask/_compat.py | 2 +- flask/helpers.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst index fb5a0c4a..0e3f7de5 100644 --- a/docs/tutorial/dbcon.rst +++ b/docs/tutorial/dbcon.rst @@ -53,7 +53,7 @@ every time the app context tears down. So what does this mean? Essentially the app context is created before the request comes in and is destroyed (teared down) whenever the request finishes. A teardown can happen because of two reasons: either everything went well (the error -parameter will be `None`) or an exception happend in which case the error +parameter will be `None`) or an exception happened in which case the error is passed to the teardown function. Curious about what these contexts mean? Have a look at the diff --git a/flask/_compat.py b/flask/_compat.py index 86a87832..d4ec9839 100644 --- a/flask/_compat.py +++ b/flask/_compat.py @@ -77,7 +77,7 @@ def with_metaclass(meta, *bases): # breaks the __exit__ function in a very peculiar way. This is currently # true for pypy 2.2.1 for instance. The second level of exception blocks # is necessary because pypy seems to forget to check if an exception -# happend until the next bytecode instruction? +# happened until the next bytecode instruction? BROKEN_PYPY_CTXMGR_EXIT = False if hasattr(sys, 'pypy_version_info'): class _Mgr(object): diff --git a/flask/helpers.py b/flask/helpers.py index 920c4c87..77148708 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -662,7 +662,7 @@ def get_root_path(import_name): 'module came from an import hook that does ' 'not provide file name information or because ' 'it\'s a namespace package. In this case ' - 'the root path needs to be explictly ' + 'the root path needs to be explicitly ' 'provided.' % import_name) # filepath is import_name.py for a module, or __init__.py for a package. From 16308bf6754facf0fec8b8e8644a0a4940cc0ff1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:13:19 +0200 Subject: [PATCH 012/731] Check links in the documentation with tox --- tox.ini | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index b09458d9..ceb42b6d 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,11 @@ [tox] -envlist = py26, py27, pypy, py33 +envlist = docs, py26, py27, pypy, py33 [testenv] deps = blinker commands = python run-tests.py [] + + +[testenv:docs] +deps = sphinx +commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/linkcheck From 20edb3189c82c5f5fddc1792930bcf5391fb6f42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:20:12 +0200 Subject: [PATCH 013/731] Fix Pocoo documentation links --- docs/index.rst | 6 +++--- docs/installation.rst | 2 +- docs/quickstart.rst | 4 ++-- docs/templating.rst | 2 +- docs/tutorial/templates.rst | 2 +- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 43702409..617104ee 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -21,11 +21,11 @@ engine and the `Werkzeug`_ WSGI toolkit. These libraries are not documented here. If you want to dive into their documentation, check out the following links: -- `Jinja2 Documentation `_ -- `Werkzeug Documentation `_ +- `Jinja2 Documentation `_ +- `Werkzeug Documentation `_ -.. _Jinja2: http://jinja.pocoo.org/2/ +.. _Jinja2: http://jinja.pocoo.org/ .. _Werkzeug: http://werkzeug.pocoo.org/ .. include:: contents.rst.inc diff --git a/docs/installation.rst b/docs/installation.rst index 78f192fd..163782f2 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -4,7 +4,7 @@ Installation ============ Flask depends on some external libraries, like `Werkzeug -`_ and `Jinja2 `_. +`_ and `Jinja2 `_. Werkzeug is a toolkit for WSGI, the standard Python interface between web applications and a variety of servers for both development and deployment. Jinja2 renders templates. diff --git a/docs/quickstart.rst b/docs/quickstart.rst index a4494f43..05e27e74 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -347,7 +347,7 @@ Rendering Templates Generating HTML from within Python is not fun, and actually pretty cumbersome because you have to do the HTML escaping on your own to keep the application secure. Because of that Flask configures the `Jinja2 -`_ template engine for you automatically. +`_ template engine for you automatically. To render a template you can use the :func:`~flask.render_template` method. All you have to do is provide the name of the template and the @@ -380,7 +380,7 @@ package it's actually inside your package: For templates you can use the full power of Jinja2 templates. Head over to the official `Jinja2 Template Documentation -`_ for more information. +`_ for more information. Here is an example template: diff --git a/docs/templating.rst b/docs/templating.rst index 4e432333..bf672672 100644 --- a/docs/templating.rst +++ b/docs/templating.rst @@ -9,7 +9,7 @@ An extension can depend on Jinja2 being present. This section only gives a very quick introduction into how Jinja2 is integrated into Flask. If you want information on the template engine's syntax itself, head over to the official `Jinja2 Template -Documentation `_ for +Documentation `_ for more information. Jinja Setup diff --git a/docs/tutorial/templates.rst b/docs/tutorial/templates.rst index 5ec5584d..fd3abe68 100644 --- a/docs/tutorial/templates.rst +++ b/docs/tutorial/templates.rst @@ -16,7 +16,7 @@ the layout of the website in all pages. Put the following templates into the `templates` folder: -.. _Jinja2: http://jinja.pocoo.org/2/documentation/templates +.. _Jinja2: http://jinja.pocoo.org/docs/templates layout.html ----------- From b5e78425be985eeec4c91ea8277368a9fae9411a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:24:07 +0200 Subject: [PATCH 014/731] Link to https version of python docs To which the http version permanently redirects --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 16c841f4..524ee555 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -244,7 +244,7 @@ latex_additional_files = ['flaskstyle.sty', 'logo.pdf'] #epub_tocdepth = 3 intersphinx_mapping = { - 'http://docs.python.org/dev': None, + 'https://docs.python.org/dev': None, 'http://werkzeug.pocoo.org/docs/': None, 'http://www.sqlalchemy.org/docs/': None, 'http://wtforms.simplecodes.com/docs/0.5/': None, From 954eeb8d1784029cb09da04eef6470036ce5dc64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:26:12 +0200 Subject: [PATCH 015/731] Fix Jinja link --- docs/tutorial/folders.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/folders.rst b/docs/tutorial/folders.rst index 4bf47cd7..e1ff229d 100644 --- a/docs/tutorial/folders.rst +++ b/docs/tutorial/folders.rst @@ -20,4 +20,4 @@ templates you create later in the tutorial will go in this directory. Continue with :ref:`tutorial-schema`. -.. _Jinja2: http://jinja.pocoo.org/2/ +.. _Jinja2: http://jinja.pocoo.org/ From 34871a286efac32b206d9175ec546df7f47dcb48 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:30:28 +0200 Subject: [PATCH 016/731] Switch pypi links to https To which the http version redirects permanently --- CHANGES | 2 +- docs/api.rst | 2 +- docs/deploying/mod_wsgi.rst | 2 +- docs/patterns/caching.rst | 2 +- docs/patterns/distribute.rst | 4 ++-- docs/patterns/sqlalchemy.rst | 2 +- docs/patterns/wtforms.rst | 2 +- docs/signals.rst | 2 +- 8 files changed, 9 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 80c4b27d..86f588cf 100644 --- a/CHANGES +++ b/CHANGES @@ -400,7 +400,7 @@ Released on July 27th 2010, codename Whisky prefix. This makes it possible to bind a whole module to a configurable subdomain. -.. _blinker: http://pypi.python.org/pypi/blinker +.. _blinker: https://pypi.python.org/pypi/blinker Version 0.5.2 ------------- diff --git a/docs/api.rst b/docs/api.rst index c87055d1..6c9f7414 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -597,7 +597,7 @@ Signals do nothing but will fail with a :exc:`RuntimeError` for all other operations, including connecting. -.. _blinker: http://pypi.python.org/pypi/blinker +.. _blinker: https://pypi.python.org/pypi/blinker Class-Based Views ----------------- diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index 8fd2c0bb..baac5a1b 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -109,7 +109,7 @@ For more information consult the `mod_wsgi wiki`_. .. _mod_wsgi: http://code.google.com/p/modwsgi/ .. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide -.. _virtual python: http://pypi.python.org/pypi/virtualenv +.. _virtual python: https://pypi.python.org/pypi/virtualenv .. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/ Troubleshooting diff --git a/docs/patterns/caching.rst b/docs/patterns/caching.rst index 5817aa29..a0633cf9 100644 --- a/docs/patterns/caching.rst +++ b/docs/patterns/caching.rst @@ -27,7 +27,7 @@ cache that keeps the item stored in the memory of the Python interpreter:: cache = SimpleCache() If you want to use memcached, make sure to have one of the memcache modules -supported (you get them from `PyPI `_) and a +supported (you get them from `PyPI `_) and a memcached server running somewhere. This is how you connect to such an memcached server then:: diff --git a/docs/patterns/distribute.rst b/docs/patterns/distribute.rst index b6f6a5ef..aa53e685 100644 --- a/docs/patterns/distribute.rst +++ b/docs/patterns/distribute.rst @@ -161,6 +161,6 @@ folder instead of copying the data over. You can then continue to work on the code without having to run `install` again after each change. -.. _distribute: http://pypi.python.org/pypi/distribute -.. _pip: http://pypi.python.org/pypi/pip +.. _distribute: https://pypi.python.org/pypi/distribute +.. _pip: https://pypi.python.org/pypi/pip .. _distribute_setup.py: http://python-distribute.org/distribute_setup.py diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index 3c4d9ce9..dd0eee91 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -20,7 +20,7 @@ there is a Flask extension that handles that for you. This is recommended if you want to get started quickly. You can download `Flask-SQLAlchemy`_ from `PyPI -`_. +`_. .. _Flask-SQLAlchemy: http://packages.python.org/Flask-SQLAlchemy/ diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst index 1bf46637..005efebb 100644 --- a/docs/patterns/wtforms.rst +++ b/docs/patterns/wtforms.rst @@ -17,7 +17,7 @@ forms. The `Flask-WTF`_ extension expands on this pattern and adds a few handful little helpers that make working with forms and Flask more fun. You can get it from `PyPI - `_. + `_. .. _Flask-WTF: http://packages.python.org/Flask-WTF/ diff --git a/docs/signals.rst b/docs/signals.rst index c9df1edf..46342033 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -349,4 +349,4 @@ The following signals exist in Flask: .. versionadded:: 0.10 -.. _blinker: http://pypi.python.org/pypi/blinker +.. _blinker: https://pypi.python.org/pypi/blinker From e89c867d62f351bad4ca4bb6bc096695e529d45d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:35:37 +0200 Subject: [PATCH 017/731] Switch packages.python.org to pythonhosted.org --- docs/extensiondev.rst | 2 +- docs/patterns/fileuploads.rst | 2 +- docs/patterns/sqlalchemy.rst | 2 +- docs/patterns/wtforms.rst | 4 ++-- docs/upgrading.rst | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/extensiondev.rst b/docs/extensiondev.rst index 09bf2d2c..4e0e45d8 100644 --- a/docs/extensiondev.rst +++ b/docs/extensiondev.rst @@ -415,6 +415,6 @@ instead of ``flask_foo`` or ``flaskext_foo`` so that extensions can transition to the new package name without affecting users. -.. _OAuth extension: http://packages.python.org/Flask-OAuth/ +.. _OAuth extension: http://pythonhosted.org/Flask-OAuth/ .. _mailinglist: http://flask.pocoo.org/mailinglist/ .. _IRC channel: http://flask.pocoo.org/community/irc/ diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index eef50a9a..74cdd98b 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -178,4 +178,4 @@ applications dealing with uploads, there is a Flask extension called `Flask-Uploads`_ that implements a full fledged upload mechanism with white and blacklisting of extensions and more. -.. _Flask-Uploads: http://packages.python.org/Flask-Uploads/ +.. _Flask-Uploads: http://pythonhosted.org/Flask-Uploads/ diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index dd0eee91..3b517a6d 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -22,7 +22,7 @@ if you want to get started quickly. You can download `Flask-SQLAlchemy`_ from `PyPI `_. -.. _Flask-SQLAlchemy: http://packages.python.org/Flask-SQLAlchemy/ +.. _Flask-SQLAlchemy: http://pythonhosted.org/Flask-SQLAlchemy/ Declarative diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst index 005efebb..8017aea5 100644 --- a/docs/patterns/wtforms.rst +++ b/docs/patterns/wtforms.rst @@ -8,7 +8,7 @@ will handle here. If you find yourself in the situation of having many forms, you might want to give it a try. When you are working with WTForms you have to define your forms as classes -first. I recommend breaking up the application into multiple modules +first. I recommend breaking up the application into multiple modules (:ref:`larger-applications`) for that and adding a separate module for the forms. @@ -19,7 +19,7 @@ forms. fun. You can get it from `PyPI `_. -.. _Flask-WTF: http://packages.python.org/Flask-WTF/ +.. _Flask-WTF: http://pythonhosted.org/Flask-WTF/ The Forms --------- diff --git a/docs/upgrading.rst b/docs/upgrading.rst index ebea0101..ccfa82ad 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -43,7 +43,7 @@ when there is no request context yet but an application context. The old ``flask.Flask.request_globals_class`` attribute was renamed to :attr:`flask.Flask.app_ctx_globals_class`. -.. _Flask-OldSessions: http://packages.python.org/Flask-OldSessions/ +.. _Flask-OldSessions: http://pythonhosted.org/Flask-OldSessions/ Version 0.9 ----------- From 52def324b882dc99e0254abcefd1ba608b29a1b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:36:36 +0200 Subject: [PATCH 018/731] Use https instead of http for docs.python.org --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 05e27e74..930a2f0c 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -818,7 +818,7 @@ Here are some example log calls:: The attached :attr:`~flask.Flask.logger` is a standard logging :class:`~logging.Logger`, so head over to the official `logging -documentation `_ for more +documentation `_ for more information. Hooking in WSGI Middlewares From 51228ad0d17eac9217cf0780bb2490a1b3a24a16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:37:19 +0200 Subject: [PATCH 019/731] Use https for devcenter.heroku.com --- docs/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/quickstart.rst b/docs/quickstart.rst index 930a2f0c..47d85ad5 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -841,7 +841,7 @@ Ready to deploy your new Flask app? To wrap up the quickstart, you can immediately deploy to a hosted platform, all of which offer a free plan for small projects: -- `Deploying Flask on Heroku `_ +- `Deploying Flask on Heroku `_ - `Deploying WSGI on dotCloud `_ with `Flask-specific notes `_ From 13293b499faccb30e63a2477cfd780d2297a6fa2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:38:20 +0200 Subject: [PATCH 020/731] Use libevent.org --- docs/deploying/wsgi-standalone.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deploying/wsgi-standalone.rst b/docs/deploying/wsgi-standalone.rst index c8d4f20e..11d07831 100644 --- a/docs/deploying/wsgi-standalone.rst +++ b/docs/deploying/wsgi-standalone.rst @@ -64,7 +64,7 @@ event loop:: .. _Gevent: http://www.gevent.org/ .. _greenlet: http://greenlet.readthedocs.org/en/latest/ -.. _libevent: http://monkey.org/~provos/libevent/ +.. _libevent: http://libevent.org/ Twisted Web ----------- From e991bef57482152639e7b797ab98f2710ab9c4c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:39:22 +0200 Subject: [PATCH 021/731] Switch diveintohtml5.org to diveintohtml5.info --- docs/htmlfaq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/htmlfaq.rst b/docs/htmlfaq.rst index b16f4cd5..434bb656 100644 --- a/docs/htmlfaq.rst +++ b/docs/htmlfaq.rst @@ -186,7 +186,7 @@ Many other features have been added, as well. A good guide to new features in HTML5 is Mark Pilgrim's soon-to-be-published book, `Dive Into HTML5`_. Not all of them are supported in browsers yet, however, so use caution. -.. _Dive Into HTML5: http://www.diveintohtml5.org/ +.. _Dive Into HTML5: http://www.diveintohtml5.info/ What should be used? -------------------- From 033b0931aaf32f1f34045270d89fc64b29835329 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sat, 26 Apr 2014 01:45:20 +0200 Subject: [PATCH 022/731] Run tests with Python 3.4 with tox --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ceb42b6d..24155a9a 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = docs, py26, py27, pypy, py33 +envlist = docs, py26, py27, pypy, py33, py34 [testenv] deps = blinker From 0ac492a0acda40208e6c613644fe9e779dc3d937 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:26:14 +0200 Subject: [PATCH 023/731] Depend on click now --- setup.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/setup.py b/setup.py index 0fbf7e8d..196eca4c 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ Links from __future__ import print_function from setuptools import Command, setup + class run_audit(Command): """Audits source code using PyFlakes for following issues: - Names which are used but not defined or used before they are defined. @@ -56,7 +57,8 @@ class run_audit(Command): pass def run(self): - import os, sys + import os + import sys try: import pyflakes.scripts.pyflakes as flakes except ImportError: @@ -69,7 +71,7 @@ class run_audit(Command): for dir in dirs: for root, _, files in os.walk(dir): for file in files: - if file != '__init__.py' and file.endswith('.py') : + if file != '__init__.py' and file.endswith('.py'): warns += flakes.checkPath(os.path.join(root, file)) if warns > 0: print("Audit finished with total %d warnings." % warns) @@ -93,7 +95,8 @@ setup( install_requires=[ 'Werkzeug>=0.7', 'Jinja2>=2.4', - 'itsdangerous>=0.21' + 'itsdangerous>=0.21', + 'click', ], classifiers=[ 'Development Status :: 4 - Beta', @@ -108,7 +111,7 @@ setup( ], entry_points=''' [console_scripts] - flask-run=flask.run:main + flask=flask.cli:main ''', cmdclass={'audit': run_audit}, test_suite='flask.testsuite.suite' From 3bdb90f06b9d3167320180d4a5055dcd949bf72f Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:26:23 +0200 Subject: [PATCH 024/731] Added click support to Flask --- flask/__main__.py | 2 +- flask/app.py | 47 ++++++- flask/cli.py | 347 ++++++++++++++++++++++++++++++++++++++++++++++ flask/run.py | 247 --------------------------------- 4 files changed, 392 insertions(+), 251 deletions(-) create mode 100644 flask/cli.py delete mode 100644 flask/run.py diff --git a/flask/__main__.py b/flask/__main__.py index e3c3bd34..cbcdebf1 100644 --- a/flask/__main__.py +++ b/flask/__main__.py @@ -11,5 +11,5 @@ if __name__ == '__main__': - from run import main + from cli import main main(as_module=True) diff --git a/flask/app.py b/flask/app.py index bb5cbbd7..3e421f3d 100644 --- a/flask/app.py +++ b/flask/app.py @@ -34,6 +34,7 @@ from .templating import DispatchingJinjaLoader, Environment, \ _default_template_ctx_processor from .signals import request_started, request_finished, got_request_exception, \ request_tearing_down, appcontext_tearing_down +from .cli import make_default_cli from ._compat import reraise, string_types, text_type, integer_types # a lock used for logger initialization @@ -476,6 +477,12 @@ class Flask(_PackageBoundObject): None: [_default_template_ctx_processor] } + #: A list of shell context processor functions that should be run + #: when a shell context is created. + #: + #: .. versionadded:: 1.0 + self.shell_context_processors = [] + #: all the attached blueprints in a dictionary by name. Blueprints #: can be attached multiple times so this dictionary does not tell #: you how often they got attached. @@ -531,6 +538,14 @@ class Flask(_PackageBoundObject): endpoint='static', view_func=self.send_static_file) + #: The click command line context for this application. Commands + #: registered here show up in the ``flask`` command once the + #: application has been discovered. The default commands are + #: provided by Flask itself and can be overridden. + #: + #: This is an instance of a :class:`click.Group` object. + self.cli = make_default_cli(self) + def _get_error_handlers(self): from warnings import warn warn(DeprecationWarning('error_handlers is deprecated, use the ' @@ -748,6 +763,18 @@ class Flask(_PackageBoundObject): # existing views. context.update(orig_ctx) + def make_shell_context(self): + """Returns the shell context for an interactive shell for this + application. This runs all the registered shell context + processors. + + .. versionadded:: 1.0 + """ + rv = {'app': self, 'g': g} + for processor in self.shell_context_processors: + rv.update(processor()) + return rv + def run(self, host=None, port=None, debug=None, **options): """Runs the application on a local development server. If the :attr:`debug` flag is set the server will automatically reload @@ -758,6 +785,11 @@ class Flask(_PackageBoundObject): ``use_evalex=False`` as parameter. This will keep the debugger's traceback screen active, but disable code execution. + It is not recommended to use this function for development with + automatic reloading as this is badly supported. Instead you should + be using the ``flask`` command line script's ``runserver`` + support. + .. admonition:: Keep in Mind Flask will suppress any server error with a generic error page @@ -1091,7 +1123,7 @@ class Flask(_PackageBoundObject): Use :meth:`register_error_handler` instead of modifying :attr:`error_handler_spec` directly, for application wide error handlers. - + .. versionadded:: 0.7 One can now additionally also register custom exception types that do not necessarily have to be a subclass of the @@ -1325,6 +1357,15 @@ class Flask(_PackageBoundObject): self.template_context_processors[None].append(f) return f + @setupmethod + def shell_context_processor(self, f): + """Registers a shell context processor function. + + .. versionadded:: 1.0 + """ + self.shell_context_processors.append(f) + return f + @setupmethod def url_value_preprocessor(self, f): """Registers a function as URL value preprocessor for all view @@ -1609,7 +1650,8 @@ class Flask(_PackageBoundObject): # some extra logic involved when creating these objects with # specific values (like default content type selection). if isinstance(rv, (text_type, bytes, bytearray)): - rv = self.response_class(rv, headers=headers, status=status_or_headers) + rv = self.response_class(rv, headers=headers, + status=status_or_headers) headers = status_or_headers = None else: rv = self.response_class.force_type(rv, request.environ) @@ -1624,7 +1666,6 @@ class Flask(_PackageBoundObject): return rv - def create_url_adapter(self, request): """Creates a URL adapter for the given request. The URL adapter is created at a point where the request context is not yet set up diff --git a/flask/cli.py b/flask/cli.py new file mode 100644 index 00000000..7e982878 --- /dev/null +++ b/flask/cli.py @@ -0,0 +1,347 @@ +# -*- coding: utf-8 -*- +""" + flask.run + ~~~~~~~~~ + + A simple command line application to run flask apps. + + :copyright: (c) 2014 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + +import os +import sys +from threading import Lock +from contextlib import contextmanager + +import click + +from ._compat import iteritems + + +class NoAppException(click.UsageError): + """Raised if an application cannot be found or loaded.""" + + +def find_best_app(module): + """Given a module instance this tries to find the best possible + application in the module or raises an exception. + """ + from . import Flask + + # Search for the most common names first. + for attr_name in 'app', 'application': + app = getattr(module, attr_name, None) + if app is not None and isinstance(app, Flask): + return app + + # Otherwise find the only object that is a Flask instance. + matches = [v for k, v in iteritems(module.__dict__) + if isinstance(v, Flask)] + + if matches: + if len(matches) > 1: + raise NoAppException('More than one possible Flask application ' + 'found in module "%s", none of which are called ' + '"app". Be explicit!' % module.__name__) + return matches[0] + + raise NoAppException('Failed to find application in module "%s". Are ' + 'you sure it contains a Flask application? Maybe ' + 'you wrapped it in a WSGI middleware or you are ' + 'using a factory function.' % module.__name__) + + +def prepare_exec_for_file(filename): + module = [] + + # Chop off file extensions or package markers + if filename.endswith('.py'): + filename = filename[:-3] + elif os.path.split(filename)[1] == '__init__.py': + filename = os.path.dirname(filename) + filename = os.path.realpath(filename) + + dirpath = filename + while 1: + dirpath, extra = os.path.split(dirpath) + module.append(extra) + if not os.path.isfile(os.path.join(dirpath, '__init__.py')): + break + + sys.path.insert(0, dirpath) + return '.'.join(module[::-1]) + + +def locate_app(app_id, debug=None): + """Attempts to locate the application.""" + if ':' in app_id: + module, app_obj = app_id.split(':', 1) + else: + module = app_id + app_obj = None + + __import__(module) + mod = sys.modules[module] + if app_obj is None: + app = find_best_app(mod) + else: + app = getattr(mod, app_obj, None) + if app is None: + raise RuntimeError('Failed to find application in module "%s"' + % module) + if debug is not None: + app.debug = debug + return app + + +class DispatchingApp(object): + """Special applicationt that dispatches to a flask application which + is imported by name on first request. This is safer than importing + the application upfront because it means that we can forward all + errors for import problems into the browser as error. + """ + + def __init__(self, app_id, debug=None, use_eager_loading=False): + self.app_id = app_id + self.app = None + self.debug = debug + self._lock = Lock() + if use_eager_loading: + self._load_unlocked() + + def _load_unlocked(self): + self.app = rv = locate_app(self.app_id, self.debug) + return rv + + def __call__(self, environ, start_response): + if self.app is not None: + return self.app(environ, start_response) + with self._lock: + if self.app is not None: + rv = self.app + else: + rv = self._load_unlocked() + return rv(environ, start_response) + + +class ScriptInfo(object): + """Help object to deal with Flask applications. This is usually not + necessary to interface with as it's used internally in the dispatching + to click. + """ + + def __init__(self): + self.app_import_path = None + self.debug = None + self._loaded_app = None + + def get_app_import_path(self): + """Return the actual application import path or fails if it is + not yet set. + """ + if self.app_import_path is not None: + return self.app_import_path + raise NoAppException('Could not locate application. ' + 'You did not provide FLASK_APP or the ' + '--app parameter.') + + def load_app(self): + """Loads the app (if not yet loaded) and returns it.""" + if self._loaded_app is not None: + return self._loaded_app + rv = locate_app(self.get_app_import_path(), self.debug) + self._loaded_app = rv + return rv + + @contextmanager + def conditional_context(self, with_context=True): + """Creates an application context or not, depending on the + given parameter but always works as context manager. + """ + if with_context: + with self.load_app().app_context() as ctx: + yield ctx + else: + yield None + + +pass_script_info = click.make_pass_decorator(ScriptInfo) + + +def without_appcontext(f): + """Marks a click callback so that it does not get a app context + created. This only works for commands directly registered to + the toplevel system. This really is only useful for very + special commands like the runserver one. + """ + f.__flask_without_appcontext__ = True + return f + + +class FlaskClickGroup(click.Group): + """Special subclass of the a regular click group that supports + loading more commands from the configured Flask app. + """ + + def __init__(self, help=None): + def set_app_id(ctx, value): + if value is not None: + if os.path.isfile(value) or os.sep in value or \ + os.altsep is not None and os.altsep in value: + value = prepare_exec_for_file(value) + ctx.obj.app_import_path = value + def set_debug(ctx, value): + ctx.obj.debug = value + + click.Group.__init__(self, help=help, params=[ + click.Option(['-a', '--app'], + help='The application to run', + callback=set_app_id, is_eager=True), + click.Option(['--debug/--no-debug'], + help='Enable or disable debug mode.', + default=None, callback=set_debug) + ]) + + def get_command(self, ctx, name): + info = ctx.find_object(ScriptInfo) + # Find the command in the application first, if we can find it. + # If the app is not available, we just ignore this silently. + try: + rv = info.load_app().cli.get_command(ctx, name) + if rv is not None: + return rv + except NoAppException: + pass + return click.Group.get_command(self, ctx, name) + + def list_commands(self, ctx): + # The commands available is the list of both the application (if + # available) plus the builtin commands. + rv = set(click.Group.list_commands(self, ctx)) + info = ctx.find_object(ScriptInfo) + try: + rv.update(info.load_app().cli.list_commands(ctx)) + except NoAppException: + pass + return sorted(rv) + + def invoke_subcommand(self, ctx, cmd, cmd_name, args): + with_context = cmd.callback is None or \ + not getattr(cmd.callback, '__flask_without_appcontext__', False) + + with ctx.find_object(ScriptInfo).conditional_context(with_context): + return click.Group.invoke_subcommand( + self, ctx, cmd, cmd_name, args) + + +cli = FlaskClickGroup(help='''\ +This shell command acts as general utility script for Flask applications. + +It loads the application configured (either through the FLASK_APP environment +variable or the --app parameter) and then provides commands either provided +by the application or Flask itself. + +The most useful commands are the "run" and "shell" command. + +Example usage: + + flask --app=hello --debug run +''') + + +@cli.command('run', short_help='Runs a development server.') +@click.option('--host', '-h', default='127.0.0.1', + help='The interface to bind to.') +@click.option('--port', '-p', default=5000, + help='The port to bind to.') +@click.option('--reload/--no-reload', default=None, + help='Enable or disable the reloader. By default the reloader ' + 'is active is debug is enabled.') +@click.option('--debugger/--no-debugger', default=None, + help='Enable or disable the debugger. By default the debugger ' + 'is active if debug is enabled.') +@click.option('--eager-loading/--lazy-loader', default=None, + help='Enable or disable eager loading. By default eager ' + 'loading is enabled if the reloader is disabled.') +@click.option('--with-threads/--without-threads', default=False, + help='Enable or disable multithreading.') +@without_appcontext +@pass_script_info +def run_command(info, host, port, reload, debugger, eager_loading, + with_threads): + """Runs a local development server for the Flask application.""" + from werkzeug.serving import run_simple + app_id = info.get_app_import_path() + if reload is None: + reload = info.debug + if debugger is None: + debugger = info.debug + if eager_loading is None: + eager_loading = not reload + + # Extra startup messages. This depends a but on Werkzeug internals to + # not double execute when the reloader kicks in. + if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': + print(' * Serving Flask app "%s"' % app_id) + if info.debug is not None: + print(' * Forcing debug %s' % (info.debug and 'on' or 'off')) + + app = DispatchingApp(app_id, info.debug, eager_loading) + run_simple(host, port, app, use_reloader=reload, + use_debugger=debugger, threaded=with_threads) + + +@cli.command('shell', short_help='Runs a shell in the app context.') +def shell_command(): + """Runs an interactive Python shell in the context of a given + Flask application. The application will populate the default + namespace of this shell according to it's configuration. + + This is useful for executing small snippets of management code + without having to manually configuring the application. + """ + import code + from flask.globals import _app_ctx_stack + app = _app_ctx_stack.top.app + banner = 'Python %s on %s\nApp: %s%s\nInstance: %s' % ( + sys.version, + sys.platform, + app.import_name, + app.debug and ' [debug]' or '', + app.instance_path, + ) + code.interact(banner=banner, local=app.make_shell_context()) + + +def make_default_cli(app): + """Creates the default click object for the app itself. Currently + there are no default commands registered because all builtin commands + are registered on the actual cmd object here. + """ + return click.Group() + + +def main(as_module=False): + this_module = __package__ + '.cli' + args = sys.argv[1:] + + if as_module: + if sys.version_info >= (2, 7): + name = 'python -m ' + this_module.rsplit('.', 1)[0] + else: + name = 'python -m ' + this_module + + # This module is always executed as "python -m flask.run" and as such + # we need to ensure that we restore the actual command line so that + # the reloader can properly operate. + sys.argv = ['-m', this_module] + sys.argv[1:] + else: + name = 'flask' + + cli.main(args=args, prog_name=name, obj=ScriptInfo(), + auto_envvar_prefix='FLASK') + + +if __name__ == '__main__': + main(as_module=True) diff --git a/flask/run.py b/flask/run.py deleted file mode 100644 index 9d86df02..00000000 --- a/flask/run.py +++ /dev/null @@ -1,247 +0,0 @@ -# -*- coding: utf-8 -*- -""" - flask.run - ~~~~~~~~~ - - A simple command line application to run flask apps. - - :copyright: (c) 2014 by Armin Ronacher. - :license: BSD, see LICENSE for more details. -""" - -import os -import sys -from threading import Lock -from optparse import OptionParser - -from werkzeug.serving import run_simple - -from ._compat import iteritems - - -def find_best_app(module): - """Given a module instance this tries to find the best possible application - in the module or raises a RuntimeError. - """ - from flask import Flask - - # The app name wins, even if it's not a flask object. - app = getattr(module, 'app', None) - if app is not None and callable(app): - return app - - # Otherwise find the first object named Flask - matches = [] - for key, value in iteritems(module.__dict__): - if isinstance(value, Flask): - matches.append(value) - - if matches: - if len(matches) > 1: - raise RuntimeError('More than one possible Flask application ' - 'found in module "%s", none of which are called ' - '"app". Be explicit!' % module) - return matches[0] - - raise RuntimeError('Failed to find application in module "%s". Are ' - 'you sure it contains a Flask application? Maybe ' - 'you wrapped it in a WSGI middleware or you are ' - 'using a factory function.' % module) - - -def prepare_exec_for_file(filename): - module = [] - - # Chop off file extensions or package markers - if filename.endswith('.py'): - filename = filename[:-3] - elif os.path.split(filename)[1] == '__init__.py': - filename = os.path.dirname(filename) - filename = os.path.realpath(filename) - - dirpath = filename - while 1: - dirpath, extra = os.path.split(dirpath) - module.append(extra) - if not os.path.isfile(os.path.join(dirpath, '__init__.py')): - break - - sys.path.insert(0, dirpath) - return '.'.join(module[::-1]) - - -def locate_app(app_id, debug=None): - """Attempts to locate the application.""" - if ':' in app_id: - module, app_obj = app_id.split(':', 1) - else: - module = app_id - app_obj = None - - __import__(module) - mod = sys.modules[module] - if app_obj is None: - app = find_best_app(mod) - else: - app = getattr(mod, app_obj, None) - if app is None: - raise RuntimeError('Failed to find application in module "%s"' - % module) - if debug is not None: - app.debug = debug - return app - - -class DispatchingApp(object): - """Special applicationt that dispatches to a flask application which - is imported by name on first request. This is safer than importing - the application upfront because it means that we can forward all - errors for import problems into the browser as error. - """ - - def __init__(self, app_id, debug=None, use_eager_loading=False): - self.app_id = app_id - self.app = None - self.debug = debug - self._lock = Lock() - if use_eager_loading: - self._load_unlocked() - - def _load_unlocked(self): - self.app = rv = locate_app(self.app_id, self.debug) - return rv - - def __call__(self, environ, start_response): - if self.app is not None: - return self.app(environ, start_response) - with self._lock: - if self.app is not None: - rv = self.app - else: - rv = self._load_unlocked() - return rv(environ, start_response) - - -def run_application(app_id, host='127.0.0.1', port=5000, debug=None, - use_reloader=False, use_debugger=False, - use_eager_loading=None, magic_app_id=True, - **options): - """Useful function to start a Werkzeug server for an application that - is known by it's import name. By default the app ID can also be a - full file name in which case Flask attempts to reconstruct the import - name from it and do the right thing. - - :param app_id: the import name of the application module. If a colon - is provided, everything afterwards is the application - object name. In case the magic app id is enabled this - can also be a filename. - :param host: the host to bind to. - :param port: the port to bind to. - :param debug: if set to something other than None then the application's - debug flag will be set to this. - :param use_reloader: enables or disables the reloader. - :param use_debugger: enables or disables the builtin debugger. - :param use_eager_loading: enables or disables eager loading. This is - normally conditional to the reloader. - :param magic_app_id: if this is enabled then the app id can also be a - filename instead of an import module and Flask - will attempt to reconstruct the import name. - :param options: the options to be forwarded to the underlying - Werkzeug server. See - :func:`werkzeug.serving.run_simple` for more - information. - """ - if magic_app_id: - if os.path.isfile(app_id) or os.sep in app_id or \ - os.altsep is not None and os.altsep in app_id: - app_id = prepare_exec_for_file(app_id) - - if use_eager_loading is None: - use_eager_loading = not use_reloader - - # Extra startup messages. This depends a but on Werkzeug internals to - # not double execute when the reloader kicks in. - if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': - print ' * Serving Flask app "%s"' % app_id - if debug is not None: - print ' * Forcing debug %s' % (debug and 'on' or 'off') - - app = DispatchingApp(app_id, debug, use_eager_loading) - run_simple(host, port, app, use_reloader=use_reloader, - use_debugger=use_debugger, **options) - - -def main(as_module=False): - this_module = __package__ + '.run' - - if as_module: - if sys.version_info >= (2, 7): - name = 'python -m ' + this_module.rsplit('.', 1)[0] - else: - name = 'python -m ' + this_module - else: - name = 'flask-run' - - parser = OptionParser(usage='%prog [options] module', prog=name) - parser.add_option('--debug', action='store_true', - dest='debug', help='Flip debug flag on. If enabled ' - 'this also affects debugger and reloader defaults.') - parser.add_option('--no-debug', action='store_false', - dest='debug', help='Flip debug flag off.') - parser.add_option('--host', default='127.0.0.1', - help='The host to bind on. (defaults to 127.0.0.1)') - parser.add_option('--port', default=5000, - help='The port to bind on. (defaults to 5000)') - parser.add_option('--with-reloader', action='store_true', - dest='with_reloader', - help='Enable the reloader.') - parser.add_option('--without-reloader', action='store_false', - dest='with_reloader', - help='Disable the reloader.') - parser.add_option('--with-debugger', action='store_true', - dest='with_debugger', - help='Enable the debugger.') - parser.add_option('--without-debugger', action='store_false', - dest='with_debugger', - help='Disable the debugger.') - parser.add_option('--with-eager-loading', action='store_true', - dest='with_eager_loading', - help='Force enable the eager-loading. This makes the ' - 'application load immediately but makes development ' - 'flows harder. It\'s not recommended to enable eager ' - 'loading when the reloader is enabled as it can lead ' - 'to unexpected crashes.') - parser.add_option('--without-eager-loading', action='store_false', - dest='with_eager_loading', - help='Disable the eager-loading.') - parser.add_option('--with-threads', action='store_true', - dest='with_threads', - help='Enable multi-threading to handle multiple ' - 'requests concurrently.') - parser.add_option('--without-threads', action='store_false', - dest='with_threads', - help='Disables multi-threading. (default)') - opts, args = parser.parse_args() - if len(args) != 1: - parser.error('Expected exactly one argument which is the import ' - 'name of the application.') - - if opts.with_debugger is None: - opts.with_debugger = opts.debug - if opts.with_reloader is None: - opts.with_reloader = opts.debug - - # This module is always executed as "python -m flask.run" and as such - # we need to ensure that we restore the actual command line so that - # the reloader can properly operate. - sys.argv = ['-m', this_module] + sys.argv[1:] - - run_application(args[0], opts.host, opts.port, debug=opts.debug, - use_reloader=opts.with_reloader, - use_debugger=opts.with_debugger, - use_eager_loading=opts.with_eager_loading, - threaded=opts.with_threads) - - -if __name__ == '__main__': - main(as_module=True) From 7bb3271f1a5556b62efb0ccecf2b1c6bc293ce8b Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:27:07 +0200 Subject: [PATCH 025/731] Added changelog entry for click --- CHANGES | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGES b/CHANGES index 80c4b27d..26050bcc 100644 --- a/CHANGES +++ b/CHANGES @@ -26,9 +26,10 @@ Version 1.0 - Added a workaround for a limitation in Python 3.3's namespace loader. - Added support for explicit root paths when using Python 3.3's namespace packages. -- Added ``flask-run`` and the ``flask.run`` module to start the local - debug server. This is recommended over the old ``flask.run()`` method - as it works faster and more reliable due to a different design. +- Added ``flask`` and the ``flask.cli`` module to start the local + debug server through the click CLI system. This is recommended over the old + ``flask.run()`` method as it works faster and more reliable due to a + different design and also replaces ``Flask-Script``. Version 0.10.2 -------------- From e9d1fc47bf42bb13fce8799ad7727206610a7eb4 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:27:13 +0200 Subject: [PATCH 026/731] Updated docs for click support --- docs/contents.rst.inc | 2 + docs/quickstart.rst | 93 ++++++++++++++++++++++++++++++------------- docs/server.rst | 50 +++++++++++++++++++++++ docs/shell.rst | 10 +++++ 4 files changed, 127 insertions(+), 28 deletions(-) create mode 100644 docs/server.rst diff --git a/docs/contents.rst.inc b/docs/contents.rst.inc index 17e79500..8b25e61d 100644 --- a/docs/contents.rst.inc +++ b/docs/contents.rst.inc @@ -23,6 +23,8 @@ instructions for web development with Flask. reqcontext blueprints extensions + cli + server shell patterns/index deploying/index diff --git a/docs/quickstart.rst b/docs/quickstart.rst index a4494f43..bce798f8 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -20,16 +20,19 @@ A minimal Flask application looks something like this:: def hello_world(): return 'Hello World!' - if __name__ == '__main__': - app.run() - Just save it as `hello.py` (or something similar) and run it with your Python interpreter. Make sure to not call your application `flask.py` because this would conflict with Flask itself. -:: +To run the application you can either use the ``flask`` command or +python's ``-m`` switch with Flask:: + + $ flask -a hello run + * Running on http://127.0.0.1:5000/ + +or alternatively:: - $ python hello.py + $ python -m flask -a hello run * Running on http://127.0.0.1:5000/ Now head over to `http://127.0.0.1:5000/ `_, and you @@ -51,10 +54,8 @@ So what did that code do? 4. The function is given a name which is also used to generate URLs for that particular function, and returns the message we want to display in the user's browser. -5. Finally we use the :meth:`~flask.Flask.run` function to run the local server - with our application. The ``if __name__ == '__main__':`` makes sure the - server only runs if the script is executed directly from the Python - interpreter and not used as an imported module. +5. Finally we use the Flask development server to run the local server + with our application. To stop the server, hit control-C. @@ -67,37 +68,73 @@ To stop the server, hit control-C. default because in debugging mode a user of the application can execute arbitrary Python code on your computer. - If you have `debug` disabled or trust the users on your network, you can - make the server publicly available simply by changing the call of the - :meth:`~flask.Flask.run` method to look like this:: + If you have the debugger disabled or trust the users on your network, + you can make the server publicly available simply by adding + ``--host=0.0.0.0`` to the command line:: - app.run(host='0.0.0.0') + flask -a hello run --host=0.0.0.0 This tells your operating system to listen on all public IPs. +What to do if the Server does not Start +--------------------------------------- + +In case the ``python -m flask`` fails or ``flask`` does not exist, +there are multiple reasons this might be the case. First of all you need +to look at the error message. + +Old Version of Flask +```````````````````` + +Versions of Flask older than 1.0 use to have different ways to start the +application. In short, the ``flask`` command did not exist, and +neither did ``python -m flask``. In that case you have two options: +either upgrade to newer Flask versions or have a look at the :ref:`server` +docs to see the alternative method for running a server. + +Python older 2.7 +```````````````` + +In case you have a version of Python older than 2.7 ``python -m flask`` +does not work. You can either use ``flask`` or ``python -m +flask.cli`` as an alternative. This is because Python before 2.7 does no +permit packages to act as executable modules. For more information see +:ref:`cli`. + +Invalid Import Name +``````````````````` + +The ``-a`` argument to ``flask`` is the name of the module to import. In +case that module is incorrectly named you will get an import error upon +start (or if debug is enabled when you navigate to the application). It +will tell you what it tried to import and why it failed. + +The most common reason is a typo or because you did not actually create an +``app`` object. .. _debug-mode: Debug Mode ---------- -The :meth:`~flask.Flask.run` method is nice to start a local -development server, but you would have to restart it manually after each -change to your code. That is not very nice and Flask can do better. If -you enable debug support the server will reload itself on code changes, -and it will also provide you with a helpful debugger if things go wrong. +The ``flask`` script is nice to start a local development server, but +you would have to restart it manually after each change to your code. +That is not very nice and Flask can do better. If you enable debug +support the server will reload itself on code changes, and it will also +provide you with a helpful debugger if things go wrong. -There are two ways to enable debugging. Either set that flag on the -application object:: +There are different ways to enable the debug mode. The most obvious one +is the ``--debug`` parameter to the ``flask`` command:: - app.debug = True - app.run() + flask --debug -a hello run -Or pass it as a parameter to run:: +This does the following things: - app.run(debug=True) +1. it activates the debugger +2. it activates the automatic reloader +3. it enables the debug mode on the Flask application. -Both methods have the exact same effect. +There are more parameters that are explained in the :ref:`server` docs. .. admonition:: Attention @@ -236,9 +273,9 @@ below. It tells Flask to behave as though it is handling a request, even though we are interacting with it through a Python shell. Have a look at the explanation below. :ref:`context-locals`). -Why would you want to build URLs using the URL reversing function :func:`~flask.url_for` -instead of hard-coding them into your templates? There are three good reasons -for this: +Why would you want to build URLs using the URL reversing function +:func:`~flask.url_for` instead of hard-coding them into your templates? +There are three good reasons for this: 1. Reversing is often more descriptive than hard-coding the URLs. More importantly, it allows you to change URLs in one go, without having to diff --git a/docs/server.rst b/docs/server.rst new file mode 100644 index 00000000..bd79b7d0 --- /dev/null +++ b/docs/server.rst @@ -0,0 +1,50 @@ +.. _server: + +Development Server +================== + +.. currentmodule:: flask + +Starting with Flask 1.0 there are multiple built-in ways to run a +development server. The best one is the ``flask`` command line utility +but you can also continue using the :meth:`Flask.run` method. + +Command Line +------------ + +The ``flask`` command line script (:ref:`cli`) is strongly recommende for +development because it provides a superior reload experience due to how it +loads the application. The basic usage is like this:: + + $ flask -a my_application --debug run + +This will enable the debugger, the reloader and then start the server on +*http://localhost:5000/*. + +The individual features of the server can be controlled by passing more +arguments to the ``run`` option. For instance the reloader can be +disabled:: + + $ flask -a my_application --debug run --no-reload + +In Code +------- + +The alternative way to start the application is through the +:meth:`Flask.run` method. This will immediately launch a local server +exactly the same way the ``flask`` script does. + +Example:: + + if __name__ == '__main__': + app.run() + +This works well for the common case but it does not work well for +development which is why from Flask 1.0 onwards the ``flask`` method is +recommended. The reason for this is that due to how the reload mechanism +works there are some bizarre side-effects (like executing certain code +twice, sometimes crashing without message or dieing when a syntax or +import error happens). + +It is however still a perfectly valid method for invoking a non automatic +reloading application. diff --git a/docs/shell.rst b/docs/shell.rst index 61b9dc05..f4522c1a 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -26,6 +26,16 @@ context. Generally it's recommended that you read the :ref:`request-context` chapter of the documentation first. +Command Line Interface +---------------------- + +Starting with Flask 1.0 the recommended way to work with the shell is the +``flask shell`` command which does a lot of this automatically for you. +For instance the shell is automatically initialized with a loaded +application context. + +For more information see :ref:`cli`. + Creating a Request Context -------------------------- From 984142072bc268008265eee9af4f8891e3c77737 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:31:21 +0200 Subject: [PATCH 027/731] Added missing documentation page --- docs/cli.rst | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 docs/cli.rst diff --git a/docs/cli.rst b/docs/cli.rst new file mode 100644 index 00000000..b0833c22 --- /dev/null +++ b/docs/cli.rst @@ -0,0 +1,101 @@ +.. _cli: + +Command Line Interface +====================== + +.. versionadded:: 1.0 + +.. currentmodule:: flask + +One of the nice new features in Flask 1.0 is the built-in integration of +the `click `_ command line interface. This +enables a wide range of new features for the Flask ecosystem and your own +applications. + +Basic Usage +----------- + +After installation of Flask you will now find a ``flask`` script installed +into your virtualenv. If you don't want to install Flask or you have a +special use-case you can also use ``python -mflask`` to accomplish exactly +the same. + +The way this script works is by providing access to all the commands on +your Flask application's :attr:`Flask.cli` instance as well as some +built-in commands that are always there. Flask extensions can also +register more commands there if they so desire. + +For the ``flask`` script to work, an application needs to be discovered. +The two most common ways are either an environment variable +(``FLASK_APP``) or the ``--app`` / ``-a`` parameter. It should be the +import path for your application or the path to a Python file. In the +latter case Flask will attempt to setup the Python path for you +automatically and discover the module name but that might not always work. + +In that imported file the name of the app needs to be called ``app`` or +optionally be specified after a colon. + +Given a ``hello.py`` file with the application in it named ``app`` this is +how it can be run. + +Environment variables (On Windows use ``set`` instead of ``export``):: + + export FLASK_APP=hello + flask run + +Parameters:: + + flask --app=hello run + +File names:: + + flask --app=hello.py run + +Virtualenv Integration +---------------------- + +If you are constantly working with a virtualenv you can also put the +``export FLASK_APP`` into your ``activate`` script by adding it to the +bottom of the file. That way every time you activate your virtualenv you +automatically also activate the correct application name. + +Debug Flag +---------- + +The ``flask`` script can be run with ``--debug`` or ``--no-debug`` to +automatically flip the debug flag of the application. This can also be +configured by setting ``FLASK_DEBUG`` to ``1`` or ``0``. + +Running a Shell +--------------- + +To run an interactive Python shell you can use the ``shell`` command:: + + flask --app=hello shell + +This will start up an interactive Python shell, setup the correct +application context and setup the local variables in the shell. This is +done by invoking the :meth:`Flask.make_shell_context` method of the +application. By default you have access to your ``app`` and :data:`g`. + +Custom Commands +--------------- + +If you want to add more commands to the shell script you can do this +easily. Flask uses `click`_ for the command interface which makes +creating custom commands very easy. For instance if you want a shell +command to initialize the database you can do this:: + + from flask import Flask + + app = Flask(__name__) + + @app.cli.command() + def initdb(): + """Initialize the database.""" + print 'Init the db' + +The command will then show up on the command line:: + + $ flask -a hello.py initdb + Init the db From ff034e42cfb665dc9123c95e51ad3d1455322bd5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 13:54:13 +0200 Subject: [PATCH 028/731] Added a missing docstring --- flask/cli.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 7e982878..58e07521 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -53,6 +53,9 @@ def find_best_app(module): def prepare_exec_for_file(filename): + """Given a filename this will try to calculate the python path, add it + to the search path and return the actual module name that is expected. + """ module = [] # Chop off file extensions or package markers @@ -187,8 +190,7 @@ class FlaskClickGroup(click.Group): def __init__(self, help=None): def set_app_id(ctx, value): if value is not None: - if os.path.isfile(value) or os.sep in value or \ - os.altsep is not None and os.altsep in value: + if os.path.isfile(value): value = prepare_exec_for_file(value) ctx.obj.app_import_path = value def set_debug(ctx, value): From bacdd076bd544ca82d05111e3a427e98e4d4c996 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 15:15:58 +0200 Subject: [PATCH 029/731] Add "." to sys.path by default --- flask/cli.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/flask/cli.py b/flask/cli.py index 58e07521..ccfb7e17 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -192,6 +192,8 @@ class FlaskClickGroup(click.Group): if value is not None: if os.path.isfile(value): value = prepare_exec_for_file(value) + elif '.' not in sys.path: + sys.path.insert(0, '.') ctx.obj.app_import_path = value def set_debug(ctx, value): ctx.obj.debug = value From a3a5075a942a321ccf67c7511822e62dc1d5858f Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 28 Apr 2014 15:18:27 +0200 Subject: [PATCH 030/731] Updated tutorial to the flask script --- docs/tutorial/dbinit.rst | 50 ++++++++++++++++++-------------------- docs/tutorial/setup.rst | 18 +++++--------- examples/flaskr/.gitignore | 1 + examples/flaskr/flaskr.py | 20 ++++++--------- 4 files changed, 38 insertions(+), 51 deletions(-) create mode 100644 examples/flaskr/.gitignore diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 980caca0..0d92f5be 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -20,28 +20,25 @@ require that we provide the path to the database which can introduce errors. It's a good idea to add a function that initializes the database for you to the application. -To do this we can create a function called `init_db` that initializes the -database. Let me show you the code first. Just add this function below -the `connect_db` function in `flaskr.py`:: - - def init_db(): - with app.app_context(): - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() - -So what's happening here? Remember how we learned last chapter that the -application context is created every time a request comes in? Here we -don't have a request yet, so we need to create the application context by -hand. Without an application context the :data:`~flask.g` object does not -know yet to which application it becomes as there could be more than one! - -The ``with app.app_context()`` statement establishes the application -context for us. In the body of the with statement the :data:`~flask.g` -object will be associated with ``app``. At the end of the with statement -the association is released and all teardown functions are executed. This -means that our database connection is disconnected after the commit. +To do this we can create a function and hook it into the ``flask`` command +that initializes the database. Let me show you the code first. Just add +this function below the `connect_db` function in `flaskr.py`:: + + @app.cli.command() + def initdb(): + """Initializes the database.""" + db = get_db() + with app.open_resource('schema.sql', mode='r') as f: + db.cursor().executescript(f.read()) + db.commit() + print 'Initialized the database.' + +The ``app.cli.command()`` decorator registers a new command with the +``flask`` script. When the command executes Flask will automatically +create a application context for us bound to the right application. +Within the function we can then access :attr:`flask.g` and other things as +we would expect. When the script ends, the application context tears down +and the database connection is released. The :func:`~flask.Flask.open_resource` method of the application object is a convenient helper function that will open a resource that the @@ -54,16 +51,15 @@ On that cursor there is a method to execute a complete script. Finally we only have to commit the changes. SQLite 3 and other transactional databases will not commit unless you explicitly tell it to. -Now it is possible to create a database by starting up a Python shell and -importing and calling that function:: +Now it is possible to create a database with the ``flask`` script:: ->>> from flaskr import init_db ->>> init_db() + flask --app=flaskr initdb + Initialized the database. .. admonition:: Troubleshooting If you get an exception later that a table cannot be found check that - you did call the `init_db` function and that your table names are + you did execute the `initdb` command and that your table names are correct (singular vs. plural for example). Continue with :ref:`tutorial-views` diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 65d4b3f2..0b76e105 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -29,7 +29,6 @@ config from the same file, in `flaskr.py`:: # Load default config and override config from an environment variable app.config.update(dict( DATABASE=os.path.join(app.root_path, 'flaskr.db'), - DEBUG=True, SECRET_KEY='development key', USERNAME='admin', PASSWORD='default' @@ -71,10 +70,7 @@ module. Flask will the initialize the variable from that module. Note that in all cases only variable names that are uppercase are considered. The ``SECRET_KEY`` is needed to keep the client-side sessions secure. -Choose that key wisely and as hard to guess and complex as possible. The -debug flag enables or disables the interactive debugger. *Never leave -debug mode activated in a production system*, because it will allow users to -execute code on the server! +Choose that key wisely and as hard to guess and complex as possible. We will also add a method that allows for easily connecting to the specified database. This can be used to open a connection on request and @@ -92,16 +88,14 @@ tuples. rv.row_factory = sqlite3.Row return rv -Finally we just add a line to the bottom of the file that fires up the -server if we want to run that file as a standalone application:: - - if __name__ == '__main__': - app.run() - With that out of the way you should be able to start up the application without problems. Do this with the following command:: - python flaskr.py + flask --app=flaskr --debug run + +The ``--debug`` flag enables or disables the interactive debugger. *Never +leave debug mode activated in a production system*, because it will allow +users to execute code on the server! You will see a message telling you that server has started along with the address at which you can access it. diff --git a/examples/flaskr/.gitignore b/examples/flaskr/.gitignore new file mode 100644 index 00000000..fb46a3af --- /dev/null +++ b/examples/flaskr/.gitignore @@ -0,0 +1 @@ +flaskr.db diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index 384162af..2cd2fd19 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -37,13 +37,14 @@ def connect_db(): return rv -def init_db(): +@app.cli.command() +def initdb(): """Creates the database tables.""" - with app.app_context(): - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() + db = get_db() + with app.open_resource('schema.sql', mode='r') as f: + db.cursor().executescript(f.read()) + db.commit() + print('Initialized the database.') def get_db(): @@ -76,7 +77,7 @@ def add_entry(): abort(401) db = get_db() db.execute('insert into entries (title, text) values (?, ?)', - [request.form['title'], request.form['text']]) + [request.form['title'], request.form['text']]) db.commit() flash('New entry was successfully posted') return redirect(url_for('show_entries')) @@ -102,8 +103,3 @@ def logout(): session.pop('logged_in', None) flash('You were logged out') return redirect(url_for('show_entries')) - - -if __name__ == '__main__': - init_db() - app.run() From ed9e458850a548ceb6027bf25a657f26595656ff Mon Sep 17 00:00:00 2001 From: Erik Rose Date: Mon, 28 Apr 2014 11:26:18 -0400 Subject: [PATCH 031/731] Fix a docstring type in cli.py. --- flask/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index ccfb7e17..4b4d934c 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -99,7 +99,7 @@ def locate_app(app_id, debug=None): class DispatchingApp(object): - """Special applicationt that dispatches to a flask application which + """Special application that dispatches to a flask application which is imported by name on first request. This is safer than importing the application upfront because it means that we can forward all errors for import problems into the browser as error. From a3ad5405a68f312f077e8d6271e694f6925c0864 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 29 Apr 2014 01:03:32 +0200 Subject: [PATCH 032/731] Updated documentation once more for new cli. --- docs/cli.rst | 29 +++++++++++++++++++++++++++++ docs/patterns/appfactories.rst | 9 +++++++-- docs/patterns/flashing.rst | 4 ---- flask/cli.py | 5 +++++ 4 files changed, 41 insertions(+), 6 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index b0833c22..0ac578e9 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -99,3 +99,32 @@ The command will then show up on the command line:: $ flask -a hello.py initdb Init the db + +Factory Functions +----------------- + +In case you are using factory functions to create your application (see +:ref:`app-factories`) you will discover that the ``flask`` command cannot +work with them directly. Flask won't be able to figure out how to +instanciate your application properly by itself. Because of this reason +the recommendation is to create a separate file that instanciates +applications. + +For instance if you have a factory function that creates an application +from a filename you could make a separate file that creates such an +application from an environment variable. + +For instance this could be a file named ``autoapp.py`` with these +contents:: + + import os + from yourapplication import create_app + app = create_app(os.environ['YOURAPPLICATION_CONFIG']) + +Once this has happened you can make the flask command automatically pick +it up:: + + export YOURAPPLICATION_CONFIG=/path/to/config.cfg + export FLASK_APP=/path/to/autoapp.py + +From this point onwards ``flask`` will find your application. diff --git a/docs/patterns/appfactories.rst b/docs/patterns/appfactories.rst index 25a90212..2f387f1a 100644 --- a/docs/patterns/appfactories.rst +++ b/docs/patterns/appfactories.rst @@ -90,11 +90,16 @@ Using Applications ------------------ So to use such an application you then have to create the application -first. Here an example `run.py` file that runs such an application:: +first in a separate file otherwise the ``flask`` command won't be able +to find it. Here an example `exampleapp.py` file that creates such +an application:: from yourapplication import create_app app = create_app('/path/to/config.cfg') - app.run() + +It can then be used with the ``flask`` command:: + + flask --app=exampleapp run Factory Improvements -------------------- diff --git a/docs/patterns/flashing.rst b/docs/patterns/flashing.rst index 5f3b02eb..475b49fa 100644 --- a/docs/patterns/flashing.rst +++ b/docs/patterns/flashing.rst @@ -38,10 +38,6 @@ So here is a full example:: return redirect(url_for('index')) return render_template('login.html', error=error) - if __name__ == "__main__": - app.run() - - And here the ``layout.html`` template which does the magic: .. sourcecode:: html+jinja diff --git a/flask/cli.py b/flask/cli.py index ccfb7e17..89d29fe7 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -63,6 +63,11 @@ def prepare_exec_for_file(filename): filename = filename[:-3] elif os.path.split(filename)[1] == '__init__.py': filename = os.path.dirname(filename) + else: + raise NoAppException('The file provided (%s) does exist but is not a ' + 'valid Python file. This means that it cannot ' + 'be used as application. Please change the ' + 'extension to .py' % filename) filename = os.path.realpath(filename) dirpath = filename From 9ab59871007c12fad03f43ac08f889e4c080e64d Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Tue, 29 Apr 2014 01:48:31 +0200 Subject: [PATCH 033/731] Updated the examples to be cli based --- examples/blueprintexample/blueprintexample.py | 6 +----- examples/flaskr/README | 13 ++++++++++--- examples/jqueryexample/jqueryexample.py | 4 ---- examples/minitwit/README | 13 ++++++++----- examples/minitwit/minitwit.py | 19 +++++++------------ examples/persona/persona.py | 4 ---- 6 files changed, 26 insertions(+), 33 deletions(-) diff --git a/examples/blueprintexample/blueprintexample.py b/examples/blueprintexample/blueprintexample.py index bc0e41d4..925f4845 100644 --- a/examples/blueprintexample/blueprintexample.py +++ b/examples/blueprintexample/blueprintexample.py @@ -4,8 +4,4 @@ from simple_page.simple_page import simple_page app = Flask(__name__) app.register_blueprint(simple_page) # Blueprint can be registered many times -app.register_blueprint(simple_page, url_prefix='/pages') - - -if __name__ == '__main__': - app.run(debug=True) \ No newline at end of file +app.register_blueprint(simple_page, url_prefix='/pages') diff --git a/examples/flaskr/README b/examples/flaskr/README index 9b5b9215..fc555388 100644 --- a/examples/flaskr/README +++ b/examples/flaskr/README @@ -13,9 +13,16 @@ export an FLASKR_SETTINGS environment variable pointing to a configuration file. - 2. now you can run the flaskr.py file with your - python interpreter and the application will - greet you on http://localhost:5000/ + 2. initialize the database with this command: + + flask --app=flaskr initdb + + 3. now you can run flaskr: + + flask --app=flaskr run + + the application will greet you on + http://localhost:5000/ ~ Is it tested? diff --git a/examples/jqueryexample/jqueryexample.py b/examples/jqueryexample/jqueryexample.py index 08164119..034cc495 100644 --- a/examples/jqueryexample/jqueryexample.py +++ b/examples/jqueryexample/jqueryexample.py @@ -23,7 +23,3 @@ def add_numbers(): @app.route('/') def index(): return render_template('index.html') - - -if __name__ == '__main__': - app.run() diff --git a/examples/minitwit/README b/examples/minitwit/README index ab946295..873ceb8e 100644 --- a/examples/minitwit/README +++ b/examples/minitwit/README @@ -14,13 +14,16 @@ export an MINITWIT_SETTINGS environment variable pointing to a configuration file. - 2. fire up a python shell and run this: + 2. fire up a shell and run this: - >>> from minitwit import init_db; init_db() + flask --app=minitwit initdb - 3. now you can run the minitwit.py file with your - python interpreter and the application will - greet you on http://localhost:5000/ + 3. now you can run minitwit: + + flask --app=minitwit run + + the application will greet you on + http://localhost:5000/ ~ Is it tested? diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py index 913d4522..0a85247b 100644 --- a/examples/minitwit/minitwit.py +++ b/examples/minitwit/minitwit.py @@ -49,13 +49,13 @@ def close_database(exception): top.sqlite_db.close() -def init_db(): +@app.cli.command() +def initdb(): """Creates the database tables.""" - with app.app_context(): - db = get_db() - with app.open_resource('schema.sql', mode='r') as f: - db.cursor().executescript(f.read()) - db.commit() + db = get_db() + with app.open_resource('schema.sql', mode='r') as f: + db.cursor().executescript(f.read()) + db.commit() def query_db(query, args=(), one=False): @@ -217,7 +217,7 @@ def register(): if not request.form['username']: error = 'You have to enter a username' elif not request.form['email'] or \ - '@' not in request.form['email']: + '@' not in request.form['email']: error = 'You have to enter a valid email address' elif not request.form['password']: error = 'You have to enter a password' @@ -248,8 +248,3 @@ def logout(): # add some filters to jinja app.jinja_env.filters['datetimeformat'] = format_datetime app.jinja_env.filters['gravatar'] = gravatar_url - - -if __name__ == '__main__': - init_db() - app.run() diff --git a/examples/persona/persona.py b/examples/persona/persona.py index d56f299a..7374c3af 100644 --- a/examples/persona/persona.py +++ b/examples/persona/persona.py @@ -53,7 +53,3 @@ def logout_handler(): """ session.clear() return 'OK' - - -if __name__ == '__main__': - app.run() From e059bf311c6a75150275b434c53544f7528749bc Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 1 May 2014 13:44:11 +0100 Subject: [PATCH 034/731] Improved support for composable cli --- flask/cli.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index b8450de1..45e68ec5 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -139,9 +139,9 @@ class ScriptInfo(object): to click. """ - def __init__(self): - self.app_import_path = None - self.debug = None + def __init__(self, app_import_path=None, debug=None): + self.app_import_path = app_import_path + self.debug = debug self._loaded_app = None def get_app_import_path(self): @@ -187,7 +187,7 @@ def without_appcontext(f): return f -class FlaskClickGroup(click.Group): +class FlaskGroup(click.Group): """Special subclass of the a regular click group that supports loading more commands from the configured Flask app. """ @@ -244,7 +244,7 @@ class FlaskClickGroup(click.Group): self, ctx, cmd, cmd_name, args) -cli = FlaskClickGroup(help='''\ +cli = FlaskGroup(help='''\ This shell command acts as general utility script for Flask applications. It loads the application configured (either through the FLASK_APP environment @@ -346,7 +346,7 @@ def main(as_module=False): # the reloader can properly operate. sys.argv = ['-m', this_module] + sys.argv[1:] else: - name = 'flask' + name = None cli.main(args=args, prog_name=name, obj=ScriptInfo(), auto_envvar_prefix='FLASK') From d2d8e66130f8aabf8e0e4b80a42352f8ef93c347 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 2 May 2014 11:46:04 +0100 Subject: [PATCH 035/731] Fixed flask tests --- examples/flaskr/flaskr.py | 11 ++++++++--- examples/flaskr/flaskr_tests.py | 3 ++- examples/minitwit/minitwit.py | 12 +++++++++--- examples/minitwit/minitwit_tests.py | 3 ++- flask/cli.py | 4 ++-- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/examples/flaskr/flaskr.py b/examples/flaskr/flaskr.py index 2cd2fd19..a38a04eb 100644 --- a/examples/flaskr/flaskr.py +++ b/examples/flaskr/flaskr.py @@ -37,13 +37,18 @@ def connect_db(): return rv -@app.cli.command() -def initdb(): - """Creates the database tables.""" +def init_db(): + """Initializes the database.""" db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() + + +@app.cli.command('initdb') +def initdb_command(): + """Creates the database tables.""" + init_db() print('Initialized the database.') diff --git a/examples/flaskr/flaskr_tests.py b/examples/flaskr/flaskr_tests.py index ab08a60a..b90a7be7 100644 --- a/examples/flaskr/flaskr_tests.py +++ b/examples/flaskr/flaskr_tests.py @@ -21,7 +21,8 @@ class FlaskrTestCase(unittest.TestCase): self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() flaskr.app.config['TESTING'] = True self.app = flaskr.app.test_client() - flaskr.init_db() + with flaskr.app.app_context(): + flaskr.init_db() def tearDown(self): """Get rid of the database again after each test.""" diff --git a/examples/minitwit/minitwit.py b/examples/minitwit/minitwit.py index 0a85247b..f772b682 100644 --- a/examples/minitwit/minitwit.py +++ b/examples/minitwit/minitwit.py @@ -49,15 +49,21 @@ def close_database(exception): top.sqlite_db.close() -@app.cli.command() -def initdb(): - """Creates the database tables.""" +def init_db(): + """Initializes the database.""" db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() +@app.cli.command('initdb') +def initdb_command(): + """Creates the database tables.""" + init_db() + print('Initialized the database.') + + def query_db(query, args=(), one=False): """Queries the database and returns a list of dictionaries.""" cur = get_db().execute(query, args) diff --git a/examples/minitwit/minitwit_tests.py b/examples/minitwit/minitwit_tests.py index 9b027629..0a1a3f67 100644 --- a/examples/minitwit/minitwit_tests.py +++ b/examples/minitwit/minitwit_tests.py @@ -20,7 +20,8 @@ class MiniTwitTestCase(unittest.TestCase): """Before each test, set up a blank database""" self.db_fd, minitwit.app.config['DATABASE'] = tempfile.mkstemp() self.app = minitwit.app.test_client() - minitwit.init_db() + with minitwit.app.app_context(): + minitwit.init_db() def tearDown(self): """Get rid of the database again after each test.""" diff --git a/flask/cli.py b/flask/cli.py index 45e68ec5..0b37e3f9 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -199,9 +199,9 @@ class FlaskGroup(click.Group): value = prepare_exec_for_file(value) elif '.' not in sys.path: sys.path.insert(0, '.') - ctx.obj.app_import_path = value + ctx.ensure_object(ScriptInfo).app_import_path = value def set_debug(ctx, value): - ctx.obj.debug = value + ctx.ensure_object(ScriptInfo).debug = value click.Group.__init__(self, help=help, params=[ click.Option(['-a', '--app'], From b9013ede2213e463c1b6d49b3651c796debc1ea4 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 2 May 2014 11:50:00 +0100 Subject: [PATCH 036/731] Clarified how to test with click --- docs/testing.rst | 3 ++- docs/tutorial/dbinit.rst | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/docs/testing.rst b/docs/testing.rst index d31be217..95b2021a 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -43,7 +43,8 @@ In order to test the application, we add a second module self.db_fd, flaskr.app.config['DATABASE'] = tempfile.mkstemp() flaskr.app.config['TESTING'] = True self.app = flaskr.app.test_client() - flaskr.init_db() + with flaskr.app.app_context(): + flaskr.init_db() def tearDown(self): os.close(self.db_fd) diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 0d92f5be..10404b3a 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -24,13 +24,16 @@ To do this we can create a function and hook it into the ``flask`` command that initializes the database. Let me show you the code first. Just add this function below the `connect_db` function in `flaskr.py`:: - @app.cli.command() - def initdb(): - """Initializes the database.""" + def init_db(): db = get_db() with app.open_resource('schema.sql', mode='r') as f: db.cursor().executescript(f.read()) db.commit() + + @app.cli.command('initdb') + def initdb_command(): + """Initializes the database.""" + init_db() print 'Initialized the database.' The ``app.cli.command()`` decorator registers a new command with the @@ -40,6 +43,10 @@ Within the function we can then access :attr:`flask.g` and other things as we would expect. When the script ends, the application context tears down and the database connection is released. +We want to keep an actual functions around that initializes the database +though so that we can easily create databases in unittests later. (For +more information see :ref:`testing`.) + The :func:`~flask.Flask.open_resource` method of the application object is a convenient helper function that will open a resource that the application provides. This function opens a file from the resource From 4532e89efe9ef1161d4cdbd12687dac0c19c49b7 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 11:34:20 +0200 Subject: [PATCH 037/731] Refactored script info to allow more advanced patterns. --- flask/cli.py | 60 +++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 0b37e3f9..2bcfdee7 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -110,38 +110,44 @@ class DispatchingApp(object): errors for import problems into the browser as error. """ - def __init__(self, app_id, debug=None, use_eager_loading=False): - self.app_id = app_id - self.app = None - self.debug = debug + def __init__(self, loader, use_eager_loading=False): + self.loader = loader + self._app = None self._lock = Lock() if use_eager_loading: self._load_unlocked() def _load_unlocked(self): - self.app = rv = locate_app(self.app_id, self.debug) + self._app = rv = self.loader() return rv def __call__(self, environ, start_response): - if self.app is not None: - return self.app(environ, start_response) + if self._app is not None: + return self._app(environ, start_response) with self._lock: - if self.app is not None: - rv = self.app + if self._app is not None: + rv = self._app else: rv = self._load_unlocked() return rv(environ, start_response) +def _no_such_app(): + raise NoAppException('Could not locate Flask application. ' + 'You did not provide FLASK_APP or the ' + '--app parameter.') + + class ScriptInfo(object): """Help object to deal with Flask applications. This is usually not necessary to interface with as it's used internally in the dispatching to click. """ - def __init__(self, app_import_path=None, debug=None): + def __init__(self, app_import_path=None, debug=None, load_callback=None): self.app_import_path = app_import_path self.debug = debug + self.load_callback = load_callback self._loaded_app = None def get_app_import_path(self): @@ -150,18 +156,33 @@ class ScriptInfo(object): """ if self.app_import_path is not None: return self.app_import_path - raise NoAppException('Could not locate application. ' - 'You did not provide FLASK_APP or the ' - '--app parameter.') + _no_such_app() def load_app(self): """Loads the app (if not yet loaded) and returns it.""" if self._loaded_app is not None: return self._loaded_app - rv = locate_app(self.get_app_import_path(), self.debug) + if self.load_callback is not None: + rv = self.load_callback() + else: + rv = locate_app(self.get_app_import_path(), self.debug) self._loaded_app = rv return rv + def make_wsgi_app(self, use_eager_loading=False): + """Returns a WSGI app that loads the actual application at a + later stage. + """ + if self.app_import_path is not None: + def loader(): + return locate_app(self.app_import_path, self.debug) + else: + if self.load_callback is None: + _no_such_app() + def loader(): + return self.load_callback() + return DispatchingApp(loader, use_eager_loading=use_eager_loading) + @contextmanager def conditional_context(self, with_context=True): """Creates an application context or not, depending on the @@ -281,7 +302,6 @@ def run_command(info, host, port, reload, debugger, eager_loading, with_threads): """Runs a local development server for the Flask application.""" from werkzeug.serving import run_simple - app_id = info.get_app_import_path() if reload is None: reload = info.debug if debugger is None: @@ -289,14 +309,20 @@ def run_command(info, host, port, reload, debugger, eager_loading, if eager_loading is None: eager_loading = not reload + app = info.make_wsgi_app(use_eager_loading=eager_loading) + # Extra startup messages. This depends a but on Werkzeug internals to # not double execute when the reloader kicks in. if os.environ.get('WERKZEUG_RUN_MAIN') != 'true': - print(' * Serving Flask app "%s"' % app_id) + # If we have an import path we can print it out now which can help + # people understand what's being served. If we do not have an + # import path because the app was loaded through a callback then + # we won't print anything. + if info.app_import_path is not None: + print(' * Serving Flask app "%s"' % info.app_import_path) if info.debug is not None: print(' * Forcing debug %s' % (info.debug and 'on' or 'off')) - app = DispatchingApp(app_id, info.debug, eager_loading) run_simple(host, port, app, use_reloader=reload, use_debugger=debugger, threaded=with_threads) From ed7b4ccac1b646aa725007db7fd3ec2d359beaf4 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 11:36:49 +0200 Subject: [PATCH 038/731] Refactored loading logic to super properly. --- flask/cli.py | 59 +++++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 28 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 2bcfdee7..adca5ca1 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -208,30 +208,7 @@ def without_appcontext(f): return f -class FlaskGroup(click.Group): - """Special subclass of the a regular click group that supports - loading more commands from the configured Flask app. - """ - - def __init__(self, help=None): - def set_app_id(ctx, value): - if value is not None: - if os.path.isfile(value): - value = prepare_exec_for_file(value) - elif '.' not in sys.path: - sys.path.insert(0, '.') - ctx.ensure_object(ScriptInfo).app_import_path = value - def set_debug(ctx, value): - ctx.ensure_object(ScriptInfo).debug = value - - click.Group.__init__(self, help=help, params=[ - click.Option(['-a', '--app'], - help='The application to run', - callback=set_app_id, is_eager=True), - click.Option(['--debug/--no-debug'], - help='Enable or disable debug mode.', - default=None, callback=set_debug) - ]) +class ContextGroupMixin(object): def get_command(self, ctx, name): info = ctx.find_object(ScriptInfo) @@ -243,12 +220,12 @@ class FlaskGroup(click.Group): return rv except NoAppException: pass - return click.Group.get_command(self, ctx, name) + return super(ContextGroupMixin, self).get_command(ctx, name) def list_commands(self, ctx): # The commands available is the list of both the application (if # available) plus the builtin commands. - rv = set(click.Group.list_commands(self, ctx)) + rv = set(super(ContextGroupMixin, self).list_commands(ctx)) info = ctx.find_object(ScriptInfo) try: rv.update(info.load_app().cli.list_commands(ctx)) @@ -261,8 +238,34 @@ class FlaskGroup(click.Group): not getattr(cmd.callback, '__flask_without_appcontext__', False) with ctx.find_object(ScriptInfo).conditional_context(with_context): - return click.Group.invoke_subcommand( - self, ctx, cmd, cmd_name, args) + return super(ContextGroupMixin, self).invoke_subcommand( + ctx, cmd, cmd_name, args) + + +class FlaskGroup(ContextGroupMixin, click.Group): + """Special subclass of the a regular click group that supports + loading more commands from the configured Flask app. + """ + + def __init__(self, help=None): + def set_app_id(ctx, value): + if value is not None: + if os.path.isfile(value): + value = prepare_exec_for_file(value) + elif '.' not in sys.path: + sys.path.insert(0, '.') + ctx.ensure_object(ScriptInfo).app_import_path = value + def set_debug(ctx, value): + ctx.ensure_object(ScriptInfo).debug = value + + click.Group.__init__(self, help=help, params=[ + click.Option(['-a', '--app'], + help='The application to run', + callback=set_app_id, is_eager=True), + click.Option(['--debug/--no-debug'], + help='Enable or disable debug mode.', + default=None, callback=set_debug) + ]) cli = FlaskGroup(help='''\ From 81576c236a4dde33aeed13c84abed0ad2e796c2f Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 13:27:41 +0200 Subject: [PATCH 039/731] Further refactored script system to allow more customization for special apps. --- flask/cli.py | 188 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 115 insertions(+), 73 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index adca5ca1..aa385011 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -144,57 +144,62 @@ class ScriptInfo(object): to click. """ - def __init__(self, app_import_path=None, debug=None, load_callback=None): + def __init__(self, app_import_path=None, debug=None, create_app=None): self.app_import_path = app_import_path self.debug = debug - self.load_callback = load_callback + self.create_app = create_app self._loaded_app = None - def get_app_import_path(self): - """Return the actual application import path or fails if it is - not yet set. - """ - if self.app_import_path is not None: - return self.app_import_path - _no_such_app() - def load_app(self): - """Loads the app (if not yet loaded) and returns it.""" + """Loads the Flask app (if not yet loaded) and returns it.""" if self._loaded_app is not None: return self._loaded_app - if self.load_callback is not None: - rv = self.load_callback() + if self.create_app is not None: + rv = self.create_app(self) else: - rv = locate_app(self.get_app_import_path(), self.debug) + if self.app_import_path is None: + _no_such_app() + rv = locate_app(self.app_import_path, self.debug) self._loaded_app = rv return rv def make_wsgi_app(self, use_eager_loading=False): - """Returns a WSGI app that loads the actual application at a - later stage. + """Returns a WSGI app that loads the actual application at a later + stage (on first request). This has the advantage over + :meth:`load_app` that if used with a WSGI server, it will allow + the server to intercept errors later during request handling + instead of dying a horrible death. + + If eager loading is disabled the loading will happen immediately. """ if self.app_import_path is not None: def loader(): return locate_app(self.app_import_path, self.debug) else: - if self.load_callback is None: + if self.create_app is None: _no_such_app() def loader(): - return self.load_callback() + return self.create_app(self) return DispatchingApp(loader, use_eager_loading=use_eager_loading) @contextmanager def conditional_context(self, with_context=True): - """Creates an application context or not, depending on the - given parameter but always works as context manager. + """Creates an application context or not, depending on the given + parameter but always works as context manager. This is just a + shortcut for a common operation. """ if with_context: - with self.load_app().app_context() as ctx: + with self.load_app(self).app_context() as ctx: yield ctx else: yield None +#: A special decorator that informs a click callback to be passed the +#: script info object as first argument. This is normally not useful +#: unless you implement very special commands like the run command which +#: does not want the application to be loaded yet. This can be combined +#: with the :func:`without_appcontext` decorator. pass_script_info = click.make_pass_decorator(ScriptInfo) @@ -208,10 +213,56 @@ def without_appcontext(f): return f -class ContextGroupMixin(object): +class FlaskGroup(click.Group): + """Special subclass of the a regular click group that supports + loading more commands from the configured Flask app. Normally a + developer does not have to interface with this class but there are + some very advanced usecases for which it makes sense to create an + instance of this. + + :param add_default_options: if this is True the app and debug option + is automatically added. + """ + + def __init__(self, add_default_options=True, + add_default_commands=True, + create_app=None, **extra): + click.Group.__init__(self, **extra) + self.create_app = create_app + if add_default_options: + self.add_app_option() + self.add_debug_option() + if add_default_commands: + self.add_command(run_command) + self.add_command(shell_command) + + def add_app_option(self): + """Adds an option to the default command that defines an import + path that points to an application. + """ + def set_app_id(ctx, value): + if value is not None: + if os.path.isfile(value): + value = prepare_exec_for_file(value) + elif '.' not in sys.path: + sys.path.insert(0, '.') + ctx.ensure_object(ScriptInfo).app_import_path = value + + self.params.append(click.Option(['-a', '--app'], + help='The application to run', + callback=set_app_id, is_eager=True)) + + def add_debug_option(self): + """Adds an option that controls the debug flag.""" + def set_debug(ctx, value): + ctx.ensure_object(ScriptInfo).debug = value + + self.params.append(click.Option(['--debug/--no-debug'], + help='Enable or disable debug mode.', + default=None, callback=set_debug)) def get_command(self, ctx, name): - info = ctx.find_object(ScriptInfo) + info = ctx.ensure_object(ScriptInfo) # Find the command in the application first, if we can find it. # If the app is not available, we just ignore this silently. try: @@ -220,13 +271,13 @@ class ContextGroupMixin(object): return rv except NoAppException: pass - return super(ContextGroupMixin, self).get_command(ctx, name) + return click.Group.get_command(self, ctx, name) def list_commands(self, ctx): # The commands available is the list of both the application (if # available) plus the builtin commands. - rv = set(super(ContextGroupMixin, self).list_commands(ctx)) - info = ctx.find_object(ScriptInfo) + rv = set(click.Group.list_commands(self, ctx)) + info = ctx.ensure_object(ScriptInfo) try: rv.update(info.load_app().cli.list_commands(ctx)) except NoAppException: @@ -238,52 +289,19 @@ class ContextGroupMixin(object): not getattr(cmd.callback, '__flask_without_appcontext__', False) with ctx.find_object(ScriptInfo).conditional_context(with_context): - return super(ContextGroupMixin, self).invoke_subcommand( - ctx, cmd, cmd_name, args) - + return click.Group.invoke_subcommand( + self, ctx, cmd, cmd_name, args) -class FlaskGroup(ContextGroupMixin, click.Group): - """Special subclass of the a regular click group that supports - loading more commands from the configured Flask app. - """ - - def __init__(self, help=None): - def set_app_id(ctx, value): - if value is not None: - if os.path.isfile(value): - value = prepare_exec_for_file(value) - elif '.' not in sys.path: - sys.path.insert(0, '.') - ctx.ensure_object(ScriptInfo).app_import_path = value - def set_debug(ctx, value): - ctx.ensure_object(ScriptInfo).debug = value + def main(self, *args, **kwargs): + obj = kwargs.get('obj') + if obj is None: + obj = ScriptInfo(create_app=self.create_app) + kwargs['obj'] = obj + kwargs.setdefault('auto_envvar_prefix', 'FLASK') + return click.Group.main(self, *args, **kwargs) - click.Group.__init__(self, help=help, params=[ - click.Option(['-a', '--app'], - help='The application to run', - callback=set_app_id, is_eager=True), - click.Option(['--debug/--no-debug'], - help='Enable or disable debug mode.', - default=None, callback=set_debug) - ]) - -cli = FlaskGroup(help='''\ -This shell command acts as general utility script for Flask applications. - -It loads the application configured (either through the FLASK_APP environment -variable or the --app parameter) and then provides commands either provided -by the application or Flask itself. - -The most useful commands are the "run" and "shell" command. - -Example usage: - - flask --app=hello --debug run -''') - - -@cli.command('run', short_help='Runs a development server.') +@click.command('run', short_help='Runs a development server.') @click.option('--host', '-h', default='127.0.0.1', help='The interface to bind to.') @click.option('--port', '-p', default=5000, @@ -303,7 +321,17 @@ Example usage: @pass_script_info def run_command(info, host, port, reload, debugger, eager_loading, with_threads): - """Runs a local development server for the Flask application.""" + """Runs a local development server for the Flask application. + + This local server is recommended for development purposes only but it + can also be used for simple intranet deployments. By default it will + not support any sort of concurrency at all to simplify debugging. This + can be changed with the --with-threads option which will enable basic + multithreading. + + The reloader and debugger are by default enabled if the debug flag of + Flask is enabled and disabled otherwise. + """ from werkzeug.serving import run_simple if reload is None: reload = info.debug @@ -330,7 +358,7 @@ def run_command(info, host, port, reload, debugger, eager_loading, use_debugger=debugger, threaded=with_threads) -@cli.command('shell', short_help='Runs a shell in the app context.') +@click.command('shell', short_help='Runs a shell in the app context.') def shell_command(): """Runs an interactive Python shell in the context of a given Flask application. The application will populate the default @@ -360,6 +388,21 @@ def make_default_cli(app): return click.Group() +cli = FlaskGroup(help='''\ +This shell command acts as general utility script for Flask applications. + +It loads the application configured (either through the FLASK_APP environment +variable or the --app parameter) and then provides commands either provided +by the application or Flask itself. + +The most useful commands are the "run" and "shell" command. + +Example usage: + + flask --app=hello --debug run +''') + + def main(as_module=False): this_module = __package__ + '.cli' args = sys.argv[1:] @@ -377,8 +420,7 @@ def main(as_module=False): else: name = None - cli.main(args=args, prog_name=name, obj=ScriptInfo(), - auto_envvar_prefix='FLASK') + cli.main(args=args, prog_name=name) if __name__ == '__main__': From 44b0aeb048c1019e3cdd42fd2bdf9c2c8ffb7429 Mon Sep 17 00:00:00 2001 From: Marc Schlaich Date: Tue, 29 Apr 2014 14:34:21 +0200 Subject: [PATCH 040/731] Fixed typo. --- flask/cli.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/cli.py b/flask/cli.py index adca5ca1..2e6c8318 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -290,7 +290,7 @@ Example usage: help='The port to bind to.') @click.option('--reload/--no-reload', default=None, help='Enable or disable the reloader. By default the reloader ' - 'is active is debug is enabled.') + 'is active if debug is enabled.') @click.option('--debugger/--no-debugger', default=None, help='Enable or disable the debugger. By default the debugger ' 'is active if debug is enabled.') From 3569fc24415f8bac7b269ec041bd1f6bc23038ce Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 21:35:51 +0200 Subject: [PATCH 041/731] Greatly refactored click integration and documented it a bit more. --- docs/api.rst | 27 ++++++++ docs/cli.rst | 114 ++++++++++++++++++++++++++++++- flask/cli.py | 188 +++++++++++++++++++++++++++++---------------------- setup.py | 2 +- 4 files changed, 249 insertions(+), 82 deletions(-) diff --git a/docs/api.rst b/docs/api.rst index 6c9f7414..945082bc 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -753,3 +753,30 @@ Full example:: .. versionadded:: 0.8 The `provide_automatic_options` functionality was added. + +Command Line Interface +---------------------- + +.. currentmodule:: flask.cli + +.. autoclass:: FlaskGroup + :members: + +.. autoclass:: ScriptInfo + :members: + +.. autofunction:: pass_script_info + +.. autofunction:: without_appcontext + +.. autofunction:: script_info_option + + A special decorator that informs a click callback to be passed the + script info object as first argument. This is normally not useful + unless you implement very special commands like the run command which + does not want the application to be loaded yet. This can be combined + with the :func:`without_appcontext` decorator. + +.. autodata:: run_command + +.. autodata:: shell_command diff --git a/docs/cli.rst b/docs/cli.rst index 0ac578e9..171352eb 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -108,7 +108,8 @@ In case you are using factory functions to create your application (see work with them directly. Flask won't be able to figure out how to instanciate your application properly by itself. Because of this reason the recommendation is to create a separate file that instanciates -applications. +applications. This is by far not the only way to make this work. Another +is the :ref:`custom-scripts` support. For instance if you have a factory function that creates an application from a filename you could make a separate file that creates such an @@ -128,3 +129,114 @@ it up:: export FLASK_APP=/path/to/autoapp.py From this point onwards ``flask`` will find your application. + +.. _custom-scripts: + +Custom Scripts +-------------- + +While the most common way is to use the ``flask`` command, you can also +make your own "driver scripts". Since Flask uses click for the scripts +there is no reason you cannot hook these scripts into any click +application. There is one big caveat and that is, that commands +registered to :attr:`Flask.cli` will expect to be (indirectly at least) +launched from a :class:`flask.cli.FlaskGroup` click group. This is +necessary so that the commands know which Flask application they have to +work with. + +To understand why you might want custom scripts you need to understand how +click finds and executes the Flask application. If you use the ``flask`` +script you specify the application to work with on the command line or +environment variable as an import name. This is simple but it has some +limitations. Primarily it does not work with application factory +functions (see :ref:`app-factories`). + +With a custom script you don't have this problem as you can fully +customize how the application will be created. This is very useful if you +write reusable applications that you want to ship to users and they should +be presented with a custom management script. + +If you are used to writing click applications this will look familiar but +at the same time, slightly different because of how commands are loaded. +We won't go into detail now about the differences but if you are curious +you can have a look at the :ref:`script-info-object` section to learn all +about it. + +To explain all of this here an example ``manage.py`` script that manages a +hypothetical wiki application. We will go through the details +afterwards:: + + import click + from flask.cli import FlaskGroup, script_info_option + + def create_wiki_app(info): + from yourwiki import create_app + config = info.data.get('config') or 'wikiconfig.py' + return create_app(config=config) + + @click.group(cls=FlaskGroup, create_app=create_wiki_app) + @script_info_option('--config', script_info_key='config') + def cli(**params): + """This is a management script for the wiki application.""" + + if __name__ == '__main__': + cli() + +That's a lot of code for not much, so let's go through all parts step by +step. + +1. At first we import regular ``click`` as well as the click extensions + from the ``flask.cli`` package. Primarily we are here interested + in the :class:`~flask.cli.FlaskGroup` click group and the + :func:`~flask.cli.script_info_option` decorator. +2. The next thing we do is defining a function that is invoked with the + script info object (:ref:`script-info-object`) from flask and it's + purpose is to fully import and create the application. This can + either directly import an application object or create it (see + :ref:`app-factories`). + + What is ``data.info``? It's a dictionary of arbitrary data on the + script info that can be filled by options or through other means. We + will come back to this later. +3. Next step is to create a :class:`FlaskGroup`. In this case we just + make an empty function with a help doc string that just does nothing + and then pass the ``create_wiki_app`` function as factory function. + + Whenever click now needs to operate on a flask application it will + call that function with the script info and ask for it to be created. +4. In step 2 you could see that the config is passed to the actual + creation function. This config comes from the :func:`script_info_option` + decorator for the main script. It accepts a ``--config`` option and + then stores it in the script info so we can use it to create the + application. +5. All is rounded up by invoking the script. + +.. _script-info-object: + +The Script Info +--------------- + +The Flask script integration might be confusing at first, but it has good +rasons it's done this way. The reason for this is that Flask wants to +both provide custom commands to click as well as not loading your +application unless it has to. The reason for this is added flexibility. + +This way an application can provide custom commands, but even in the +absence of an application the ``flask`` script is still operational on a +basic level. In addition to that does it mean that the individual +commands have the option to not create an instance of the Flask +application unless required. This is very useful as it allows the server +command for instance, the load the application on first request instead of +immediately to give a better debug experience. + +All of this is provided through the :class:`flask.cli.ScriptInfo` object +and some helper utilities around. The basic way it operates is that when +the :class:`flask.cli.FlaskGroup` executes as a script it creates a script +info and keeps it around. From that point onwards modifications on the +script info can be done through click options. To simplify this pattern +the :func:`flask.cli.script_info_option` decorator was added. + +One Flask actually needs the individual Flask application it will invoke +the :meth:`flask.cli.ScriptInfo.load_app` method. This happens when the +server starts, when the shell is launched or when the script looks for an +application provided click command. diff --git a/flask/cli.py b/flask/cli.py index aa385011..a1308e85 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -81,7 +81,7 @@ def prepare_exec_for_file(filename): return '.'.join(module[::-1]) -def locate_app(app_id, debug=None): +def locate_app(app_id): """Attempts to locate the application.""" if ':' in app_id: module, app_obj = app_id.split(':', 1) @@ -98,8 +98,7 @@ def locate_app(app_id, debug=None): if app is None: raise RuntimeError('Failed to find application in module "%s"' % module) - if debug is not None: - app.debug = debug + return app @@ -145,13 +144,24 @@ class ScriptInfo(object): """ def __init__(self, app_import_path=None, debug=None, create_app=None): + #: The application import path self.app_import_path = app_import_path + #: The debug flag. If this is not None, the application will + #: automatically have it's debug flag overridden with this value. self.debug = debug + #: Optionally a function that is passed the script info to create + #: the instance of the application. self.create_app = create_app + #: A dictionary with arbitrary data that can be associated with + #: this script info. + self.data = {} self._loaded_app = None def load_app(self): - """Loads the Flask app (if not yet loaded) and returns it.""" + """Loads the Flask app (if not yet loaded) and returns it. Calling + this multiple times will just result in the already loaded app to + be returned. + """ if self._loaded_app is not None: return self._loaded_app if self.create_app is not None: @@ -159,29 +169,12 @@ class ScriptInfo(object): else: if self.app_import_path is None: _no_such_app() - rv = locate_app(self.app_import_path, self.debug) + rv = locate_app(self.app_import_path) + if self.debug is not None: + rv.debug = self.debug self._loaded_app = rv return rv - def make_wsgi_app(self, use_eager_loading=False): - """Returns a WSGI app that loads the actual application at a later - stage (on first request). This has the advantage over - :meth:`load_app` that if used with a WSGI server, it will allow - the server to intercept errors later during request handling - instead of dying a horrible death. - - If eager loading is disabled the loading will happen immediately. - """ - if self.app_import_path is not None: - def loader(): - return locate_app(self.app_import_path, self.debug) - else: - if self.create_app is None: - _no_such_app() - def loader(): - return self.create_app(self) - return DispatchingApp(loader, use_eager_loading=use_eager_loading) - @contextmanager def conditional_context(self, with_context=True): """Creates an application context or not, depending on the given @@ -189,17 +182,12 @@ class ScriptInfo(object): shortcut for a common operation. """ if with_context: - with self.load_app(self).app_context() as ctx: + with self.load_app().app_context() as ctx: yield ctx else: yield None -#: A special decorator that informs a click callback to be passed the -#: script info object as first argument. This is normally not useful -#: unless you implement very special commands like the run command which -#: does not want the application to be loaded yet. This can be combined -#: with the :func:`without_appcontext` decorator. pass_script_info = click.make_pass_decorator(ScriptInfo) @@ -213,54 +201,65 @@ def without_appcontext(f): return f +def set_debug_value(ctx, value): + ctx.ensure_object(ScriptInfo).debug = value + + +def set_app_value(ctx, value): + if value is not None: + if os.path.isfile(value): + value = prepare_exec_for_file(value) + elif '.' not in sys.path: + sys.path.insert(0, '.') + ctx.ensure_object(ScriptInfo).app_import_path = value + + +debug_option = click.Option(['--debug/--no-debug'], + help='Enable or disable debug mode.', + default=None, callback=set_debug_value) + + +app_option = click.Option(['-a', '--app'], + help='The application to run', + callback=set_app_value, is_eager=True) + + class FlaskGroup(click.Group): - """Special subclass of the a regular click group that supports - loading more commands from the configured Flask app. Normally a - developer does not have to interface with this class but there are - some very advanced usecases for which it makes sense to create an - instance of this. - - :param add_default_options: if this is True the app and debug option - is automatically added. + """Special subclass of the a regular click group that supports loading + more commands from the configured Flask app. Normally a developer + does not have to interface with this class but there are some very + advanced usecases for which it makes sense to create an instance of + this. + + For information as of why this is useful see :ref:`custom-scripts`. + + :param add_default_commands: if this is True then the default run and + shell commands wil be added. + :param add_app_option: adds the default ``--app`` option. This gets + automatically disabled if a `create_app` + callback is defined. + :param add_debug_option: adds the default ``--debug`` option. + :param create_app: an optional callback that is passed the script info + and returns the loaded app. """ - def __init__(self, add_default_options=True, - add_default_commands=True, - create_app=None, **extra): - click.Group.__init__(self, **extra) + def __init__(self, add_default_commands=True, add_app_option=None, + add_debug_option=True, create_app=None, **extra): + params = list(extra.pop('params', None) or ()) + if add_app_option is None: + add_app_option = create_app is None + if add_app_option: + params.append(app_option) + if add_debug_option: + params.append(debug_option) + + click.Group.__init__(self, params=params, **extra) self.create_app = create_app - if add_default_options: - self.add_app_option() - self.add_debug_option() + if add_default_commands: self.add_command(run_command) self.add_command(shell_command) - def add_app_option(self): - """Adds an option to the default command that defines an import - path that points to an application. - """ - def set_app_id(ctx, value): - if value is not None: - if os.path.isfile(value): - value = prepare_exec_for_file(value) - elif '.' not in sys.path: - sys.path.insert(0, '.') - ctx.ensure_object(ScriptInfo).app_import_path = value - - self.params.append(click.Option(['-a', '--app'], - help='The application to run', - callback=set_app_id, is_eager=True)) - - def add_debug_option(self): - """Adds an option that controls the debug flag.""" - def set_debug(ctx, value): - ctx.ensure_object(ScriptInfo).debug = value - - self.params.append(click.Option(['--debug/--no-debug'], - help='Enable or disable debug mode.', - default=None, callback=set_debug)) - def get_command(self, ctx, name): info = ctx.ensure_object(ScriptInfo) # Find the command in the application first, if we can find it. @@ -301,6 +300,33 @@ class FlaskGroup(click.Group): return click.Group.main(self, *args, **kwargs) +def script_info_option(*args, **kwargs): + """This decorator works exactly like :func:`click.option` but is eager + by default and stores the value in the :attr:`ScriptInfo.data`. This + is useful to further customize an application factory in very complex + situations. + + :param script_info_key: this is a mandatory keyword argument which + defines under which data key the value should + be stored. + """ + try: + key = kwargs.pop('script_info_key') + except LookupError: + raise TypeError('script_info_key not provided.') + + real_callback = kwargs.get('callback') + def callback(ctx, value): + if real_callback is not None: + value = real_callback(ctx, value) + ctx.ensure_object(ScriptInfo).data[key] = value + return value + + kwargs['callback'] = callback + kwargs.setdefault('is_eager', True) + return click.option(*args, **kwargs) + + @click.command('run', short_help='Runs a development server.') @click.option('--host', '-h', default='127.0.0.1', help='The interface to bind to.') @@ -340,7 +366,7 @@ def run_command(info, host, port, reload, debugger, eager_loading, if eager_loading is None: eager_loading = not reload - app = info.make_wsgi_app(use_eager_loading=eager_loading) + app = DispatchingApp(info.load_app, use_eager_loading=eager_loading) # Extra startup messages. This depends a but on Werkzeug internals to # not double execute when the reloader kicks in. @@ -388,19 +414,21 @@ def make_default_cli(app): return click.Group() -cli = FlaskGroup(help='''\ -This shell command acts as general utility script for Flask applications. +@click.group(cls=FlaskGroup) +def cli(**params): + """ + This shell command acts as general utility script for Flask applications. -It loads the application configured (either through the FLASK_APP environment -variable or the --app parameter) and then provides commands either provided -by the application or Flask itself. + It loads the application configured (either through the FLASK_APP environment + variable or the --app parameter) and then provides commands either provided + by the application or Flask itself. -The most useful commands are the "run" and "shell" command. + The most useful commands are the "run" and "shell" command. -Example usage: + Example usage: - flask --app=hello --debug run -''') + flask --app=hello --debug run + """ def main(as_module=False): diff --git a/setup.py b/setup.py index 196eca4c..1df413b2 100644 --- a/setup.py +++ b/setup.py @@ -96,7 +96,7 @@ setup( 'Werkzeug>=0.7', 'Jinja2>=2.4', 'itsdangerous>=0.21', - 'click', + 'click>=0.6', ], classifiers=[ 'Development Status :: 4 - Beta', From 2639a23b8875abb6c1dd2889728920cce5d09632 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 21:51:12 +0200 Subject: [PATCH 042/731] Updated setup.py a bit. This fixes #1018 --- setup.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/setup.py b/setup.py index 1df413b2..0a5eb538 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,8 @@ intentions. And before you ask: It's BSD licensed! Flask is Fun ```````````` +Save in a hello.py: + .. code:: python from flask import Flask @@ -23,6 +25,8 @@ Flask is Fun And Easy to Setup ````````````````` +And run it: + .. code:: bash $ pip install Flask From 1b77a3fb11a0819114804e4f88233b2dc215b653 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Wed, 7 May 2014 22:26:50 +0200 Subject: [PATCH 043/731] Made errors for working outside of contexts clearer. --- flask/globals.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/flask/globals.py b/flask/globals.py index fecbfc10..03c2f277 100644 --- a/flask/globals.py +++ b/flask/globals.py @@ -14,24 +14,41 @@ from functools import partial from werkzeug.local import LocalStack, LocalProxy +_request_ctx_err_msg = '''\ +Working outside of request context. + +This typically means that you attempted to use functionality that needed +an active HTTP request. Consult the documentation on testing for +information about how to avoid this problem.\ +''' +_app_ctx_err_msg = '''\ +Working outside of application context. + +This typically means that you attempted to use functionality that needed +to interface with the current application object in a way. To solve +this set up an application context with app.app_context(). See the +documentation for more information.\ +''' + + def _lookup_req_object(name): top = _request_ctx_stack.top if top is None: - raise RuntimeError('working outside of request context') + raise RuntimeError(_request_ctx_err_msg) return getattr(top, name) def _lookup_app_object(name): top = _app_ctx_stack.top if top is None: - raise RuntimeError('working outside of application context') + raise RuntimeError(_app_ctx_err_msg) return getattr(top, name) def _find_app(): top = _app_ctx_stack.top if top is None: - raise RuntimeError('working outside of application context') + raise RuntimeError(_app_ctx_err_msg) return top.app From a700cb2a76c0f6037f6ddaa6a519fcba179efd41 Mon Sep 17 00:00:00 2001 From: Julen Ruiz Aizpuru Date: Wed, 7 May 2014 22:32:43 +0200 Subject: [PATCH 044/731] Docs: fixes --- docs/cli.rst | 33 ++++++++++++++++----------------- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 171352eb..477802eb 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -23,7 +23,7 @@ the same. The way this script works is by providing access to all the commands on your Flask application's :attr:`Flask.cli` instance as well as some built-in commands that are always there. Flask extensions can also -register more commands there if they so desire. +register more commands there if they desire so. For the ``flask`` script to work, an application needs to be discovered. The two most common ways are either an environment variable @@ -106,8 +106,8 @@ Factory Functions In case you are using factory functions to create your application (see :ref:`app-factories`) you will discover that the ``flask`` command cannot work with them directly. Flask won't be able to figure out how to -instanciate your application properly by itself. Because of this reason -the recommendation is to create a separate file that instanciates +instantiate your application properly by itself. Because of this reason +the recommendation is to create a separate file that instantiates applications. This is by far not the only way to make this work. Another is the :ref:`custom-scripts` support. @@ -115,8 +115,7 @@ For instance if you have a factory function that creates an application from a filename you could make a separate file that creates such an application from an environment variable. -For instance this could be a file named ``autoapp.py`` with these -contents:: +This could be a file named ``autoapp.py`` with these contents:: import os from yourapplication import create_app @@ -162,8 +161,8 @@ We won't go into detail now about the differences but if you are curious you can have a look at the :ref:`script-info-object` section to learn all about it. -To explain all of this here an example ``manage.py`` script that manages a -hypothetical wiki application. We will go through the details +To explain all of this, here is an example ``manage.py`` script that +manages a hypothetical wiki application. We will go through the details afterwards:: import click @@ -185,12 +184,12 @@ afterwards:: That's a lot of code for not much, so let's go through all parts step by step. -1. At first we import regular ``click`` as well as the click extensions +1. First we import the ``click`` library as well as the click extensions from the ``flask.cli`` package. Primarily we are here interested in the :class:`~flask.cli.FlaskGroup` click group and the :func:`~flask.cli.script_info_option` decorator. 2. The next thing we do is defining a function that is invoked with the - script info object (:ref:`script-info-object`) from flask and it's + script info object (:ref:`script-info-object`) from Flask and its purpose is to fully import and create the application. This can either directly import an application object or create it (see :ref:`app-factories`). @@ -200,9 +199,9 @@ step. will come back to this later. 3. Next step is to create a :class:`FlaskGroup`. In this case we just make an empty function with a help doc string that just does nothing - and then pass the ``create_wiki_app`` function as factory function. + and then pass the ``create_wiki_app`` function as a factory function. - Whenever click now needs to operate on a flask application it will + Whenever click now needs to operate on a Flask application it will call that function with the script info and ask for it to be created. 4. In step 2 you could see that the config is passed to the actual creation function. This config comes from the :func:`script_info_option` @@ -223,11 +222,11 @@ application unless it has to. The reason for this is added flexibility. This way an application can provide custom commands, but even in the absence of an application the ``flask`` script is still operational on a -basic level. In addition to that does it mean that the individual -commands have the option to not create an instance of the Flask -application unless required. This is very useful as it allows the server -command for instance, the load the application on first request instead of -immediately to give a better debug experience. +basic level. In addition to that it means that the individual commands +have the option to avoid creating an instance of the Flask application +unless required. This is very useful as it allows the server commands for +instance to load the application on a first request instead of +immediately, therefore giving a better debug experience. All of this is provided through the :class:`flask.cli.ScriptInfo` object and some helper utilities around. The basic way it operates is that when @@ -239,4 +238,4 @@ the :func:`flask.cli.script_info_option` decorator was added. One Flask actually needs the individual Flask application it will invoke the :meth:`flask.cli.ScriptInfo.load_app` method. This happens when the server starts, when the shell is launched or when the script looks for an -application provided click command. +application-provided click command. From fa6eded6f572dd4bc23b030f025156cdd1e63305 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Thu, 8 May 2014 22:33:58 +0200 Subject: [PATCH 045/731] Fixed the cli system failing syntax errors. --- flask/cli.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index 5701e9f2..7415bfea 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -261,16 +261,24 @@ class FlaskGroup(click.Group): self.add_command(shell_command) def get_command(self, ctx, name): + # We load built-in commands first as these should always be the + # same no matter what the app does. If the app does want to + # override this it needs to make a custom instance of this group + # and not attach the default commands. + # + # This also means that the script stays functional in case the + # application completely fails. + rv = click.Group.get_command(self, ctx, name) + if rv is not None: + return rv + info = ctx.ensure_object(ScriptInfo) - # Find the command in the application first, if we can find it. - # If the app is not available, we just ignore this silently. try: rv = info.load_app().cli.get_command(ctx, name) if rv is not None: return rv except NoAppException: pass - return click.Group.get_command(self, ctx, name) def list_commands(self, ctx): # The commands available is the list of both the application (if @@ -279,7 +287,11 @@ class FlaskGroup(click.Group): info = ctx.ensure_object(ScriptInfo) try: rv.update(info.load_app().cli.list_commands(ctx)) - except NoAppException: + except Exception: + # Here we intentionally swallow all exceptions as we don't + # want the help page to break if the app does not exist. + # If someone attempts to use the command we try to create + # the app again and this will give us the error. pass return sorted(rv) From 224367fe98a056ffda7452eddd8f7c33fa2214c1 Mon Sep 17 00:00:00 2001 From: Miguel Grinberg Date: Fri, 9 May 2014 07:48:08 -0700 Subject: [PATCH 046/731] fixed import for python 3 --- flask/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/__main__.py b/flask/__main__.py index cbcdebf1..78362293 100644 --- a/flask/__main__.py +++ b/flask/__main__.py @@ -11,5 +11,5 @@ if __name__ == '__main__': - from cli import main + from .cli import main main(as_module=True) From 4a6f932610b3e3b7858017ab1cc4c20bd99ee008 Mon Sep 17 00:00:00 2001 From: lord63 Date: Sun, 11 May 2014 14:08:35 +0800 Subject: [PATCH 047/731] fix a small mistake in setup.rst --- docs/tutorial/setup.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 0b76e105..41c22e6e 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -66,7 +66,7 @@ if no such environment key is set. In addition to that you can use the :meth:`~flask.Config.from_object` method on the config object and provide it with an import name of a -module. Flask will the initialize the variable from that module. Note +module. Flask will then initialize the variable from that module. Note that in all cases only variable names that are uppercase are considered. The ``SECRET_KEY`` is needed to keep the client-side sessions secure. From fcec4b140c49ab87ca761e70c4ab98f5417a2744 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Sun, 11 May 2014 13:46:58 +0100 Subject: [PATCH 048/731] Fix a tiny spelling error: untilization -> utilization --- docs/python3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/python3.rst b/docs/python3.rst index e2556461..8173dd45 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -19,7 +19,7 @@ In addition to that you need to use the latest and greatest versions of API Stability ------------- -Some of the decisions made in regards to unicode and byte untilization on +Some of the decisions made in regards to unicode and byte utilization on Python 3 make it hard to write low level code. This mainly affects WSGI middlewares and interacting with the WSGI provided information. Werkzeug wraps all that information in high-level helpers but some of those were From 7321a480ea60ee212b06cd6698e1e3ab62b5e7f5 Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 12 May 2014 02:13:32 +0200 Subject: [PATCH 049/731] Simplified click integration a bit --- flask/app.py | 4 ++-- flask/cli.py | 38 ++++++++++++-------------------------- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/flask/app.py b/flask/app.py index c7196be4..b397f38d 100644 --- a/flask/app.py +++ b/flask/app.py @@ -11,6 +11,7 @@ import os import sys +import click from threading import Lock from datetime import timedelta from itertools import chain @@ -34,7 +35,6 @@ from .templating import DispatchingJinjaLoader, Environment, \ _default_template_ctx_processor from .signals import request_started, request_finished, got_request_exception, \ request_tearing_down, appcontext_tearing_down -from .cli import make_default_cli from ._compat import reraise, string_types, text_type, integer_types # a lock used for logger initialization @@ -544,7 +544,7 @@ class Flask(_PackageBoundObject): #: provided by Flask itself and can be overridden. #: #: This is an instance of a :class:`click.Group` object. - self.cli = make_default_cli(self) + self.cli = click.Group(self) def _get_error_handlers(self): from warnings import warn diff --git a/flask/cli.py b/flask/cli.py index 7415bfea..ec8c5ca5 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -131,12 +131,6 @@ class DispatchingApp(object): return rv(environ, start_response) -def _no_such_app(): - raise NoAppException('Could not locate Flask application. ' - 'You did not provide FLASK_APP or the ' - '--app parameter.') - - class ScriptInfo(object): """Help object to deal with Flask applications. This is usually not necessary to interface with as it's used internally in the dispatching @@ -168,7 +162,9 @@ class ScriptInfo(object): rv = self.create_app(self) else: if self.app_import_path is None: - _no_such_app() + raise NoAppException('Could not locate Flask application. ' + 'You did not provide FLASK_APP or the ' + '--app parameter.') rv = locate_app(self.app_import_path) if self.debug is not None: rv.debug = self.debug @@ -418,29 +414,19 @@ def shell_command(): code.interact(banner=banner, local=app.make_shell_context()) -def make_default_cli(app): - """Creates the default click object for the app itself. Currently - there are no default commands registered because all builtin commands - are registered on the actual cmd object here. - """ - return click.Group() +cli = FlaskGroup(help="""\ +This shell command acts as general utility script for Flask applications. +It loads the application configured (either through the FLASK_APP environment +variable or the --app parameter) and then provides commands either provided +by the application or Flask itself. -@click.group(cls=FlaskGroup) -def cli(**params): - """ - This shell command acts as general utility script for Flask applications. +The most useful commands are the "run" and "shell" command. - It loads the application configured (either through the FLASK_APP environment - variable or the --app parameter) and then provides commands either provided - by the application or Flask itself. +Example usage: - The most useful commands are the "run" and "shell" command. - - Example usage: - - flask --app=hello --debug run - """ + flask --app=hello --debug run +""") def main(as_module=False): From 937ad10275af3e190dcb63df926812a57d1e3e7e Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Mon, 12 May 2014 02:16:13 +0200 Subject: [PATCH 050/731] Simplified app finding for cli --- flask/cli.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/flask/cli.py b/flask/cli.py index ec8c5ca5..6d393345 100644 --- a/flask/cli.py +++ b/flask/cli.py @@ -39,13 +39,8 @@ def find_best_app(module): matches = [v for k, v in iteritems(module.__dict__) if isinstance(v, Flask)] - if matches: - if len(matches) > 1: - raise NoAppException('More than one possible Flask application ' - 'found in module "%s", none of which are called ' - '"app". Be explicit!' % module.__name__) + if len(matches) == 1: return matches[0] - raise NoAppException('Failed to find application in module "%s". Are ' 'you sure it contains a Flask application? Maybe ' 'you wrapped it in a WSGI middleware or you are ' From 2b8a281c1c73aa31ea81fc4e77c26d741bda87a6 Mon Sep 17 00:00:00 2001 From: lord63 Date: Mon, 12 May 2014 16:01:53 +0800 Subject: [PATCH 051/731] fix a small mistake in setup.rst --- docs/tutorial/setup.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/setup.rst b/docs/tutorial/setup.rst index 41c22e6e..1fe4ea59 100644 --- a/docs/tutorial/setup.rst +++ b/docs/tutorial/setup.rst @@ -4,7 +4,7 @@ Step 2: Application Setup Code ============================== Now that we have the schema in place we can create the application module. -Let's call it flaskr.py. We will place this file inside the flask folder. +Let's call it flaskr.py. We will place this file inside the flaskr folder. We will begin by adding the imports we need and by adding the config section. For small applications, it is possible to drop the configuration directly into the module, and this is what we will be doing here. However From 367a168d867d9b1b36485c98d9c6e5ade1d85686 Mon Sep 17 00:00:00 2001 From: Ian Connolly Date: Tue, 13 May 2014 22:04:05 +0100 Subject: [PATCH 052/731] Fixes #1051 --- docs/cli.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/cli.rst b/docs/cli.rst index 477802eb..f1d8976d 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -17,7 +17,7 @@ Basic Usage After installation of Flask you will now find a ``flask`` script installed into your virtualenv. If you don't want to install Flask or you have a -special use-case you can also use ``python -mflask`` to accomplish exactly +special use-case you can also use ``python -m flask`` to accomplish exactly the same. The way this script works is by providing access to all the commands on @@ -215,8 +215,8 @@ step. The Script Info --------------- -The Flask script integration might be confusing at first, but it has good -rasons it's done this way. The reason for this is that Flask wants to +The Flask script integration might be confusing at first, but there is a reason +why it's done this way. The reason for this is that Flask wants to both provide custom commands to click as well as not loading your application unless it has to. The reason for this is added flexibility. From 1f5927eee2288b4aaf508af5dc1f148aa2140d91 Mon Sep 17 00:00:00 2001 From: Shalabh Aggarwal Date: Wed, 14 May 2014 09:15:34 +0530 Subject: [PATCH 053/731] Fixed a small typo with flask.g docstring --- flask/app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/app.py b/flask/app.py index b397f38d..d6371c43 100644 --- a/flask/app.py +++ b/flask/app.py @@ -167,7 +167,7 @@ class Flask(_PackageBoundObject): #: #: In Flask 0.9 this property was called `request_globals_class` but it #: was changed in 0.10 to :attr:`app_ctx_globals_class` because the - #: flask.g object is not application context scoped. + #: flask.g object is now application context scoped. #: #: .. versionadded:: 0.10 app_ctx_globals_class = _AppCtxGlobals From 02b02543b34596e576c2e3d78c56bf85600f0c5a Mon Sep 17 00:00:00 2001 From: Wing Date: Sat, 17 May 2014 14:44:34 +0800 Subject: [PATCH 054/731] Doc for add check upload file --- docs/patterns/fileuploads.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/docs/patterns/fileuploads.rst b/docs/patterns/fileuploads.rst index 74cdd98b..00f44a93 100644 --- a/docs/patterns/fileuploads.rst +++ b/docs/patterns/fileuploads.rst @@ -56,7 +56,16 @@ the file and redirects the user to the URL for the uploaded file:: @app.route('/', methods=['GET', 'POST']) def upload_file(): if request.method == 'POST': + # check if the post request has the file part + if 'file' not in request.files: + flash('No file part') + return redirect(request.url) file = request.files['file'] + # if user does not select file, browser also + # submit a empty part without filename + if file.filename == '': + flash('No selected file') + return redirect(request.url) if file and allowed_file(file.filename): filename = secure_filename(file.filename) file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename)) From 305ccd5df962eb7d2c9e3d95cfd8eeb280f949e5 Mon Sep 17 00:00:00 2001 From: kekumu Date: Mon, 19 May 2014 01:48:11 -0400 Subject: [PATCH 055/731] Update advanced_foreword.rst I think the doc is supposed to read, "Because of that, Flask has a few design choices that some people might find surprising or unorthodox." Otherwise, it could possibly mean, "Because of that, Flask has few design choices that people would find surprising or unorthodox." Or perhaps even less ambiguous, "Because of that, Flask has few surprising or unorthodox design choices." --- docs/advanced_foreword.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced_foreword.rst b/docs/advanced_foreword.rst index 53df8175..9f7a73bd 100644 --- a/docs/advanced_foreword.rst +++ b/docs/advanced_foreword.rst @@ -8,7 +8,7 @@ Thread-Locals in Flask One of the design decisions in Flask was that simple tasks should be simple; they should not take a lot of code and yet they should not limit you. Because -of that, Flask has few design choices that some people might find surprising or +of that, Flask has a few design choices that some people might find surprising or unorthodox. For example, Flask uses thread-local objects internally so that you don’t have to pass objects around from function to function within a request in order to stay threadsafe. This approach is convenient, but requires a valid From bd888eaa61cb32b33dbf1e4cb327eff530bb9955 Mon Sep 17 00:00:00 2001 From: Bulat Bochkariov Date: Tue, 20 May 2014 21:54:54 -0700 Subject: [PATCH 056/731] Fixed a typo. --- docs/blueprints.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blueprints.rst b/docs/blueprints.rst index 8f83e120..2486b6ec 100644 --- a/docs/blueprints.rst +++ b/docs/blueprints.rst @@ -97,7 +97,7 @@ these:: ' (HEAD, OPTIONS, GET) -> simple_page.show>, simple_page.show>] -The first one is obviously from the application ifself for the static +The first one is obviously from the application itself for the static files. The other two are for the `show` function of the ``simple_page`` blueprint. As you can see, they are also prefixed with the name of the blueprint and separated by a dot (``.``). From fb5d6744889e834f759922e7c95b9f315384b264 Mon Sep 17 00:00:00 2001 From: David Branner Date: Fri, 30 May 2014 12:48:49 -0400 Subject: [PATCH 057/731] add `Table` to import in `sqlalchemy.rst` example --- docs/patterns/sqlalchemy.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index 07a762d8..fef9b2e9 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -177,7 +177,7 @@ SQL Abstraction Layer If you just want to use the database system (and SQL) abstraction layer you basically only need the engine:: - from sqlalchemy import create_engine, MetaData + from sqlalchemy import create_engine, MetaData, Table engine = create_engine('sqlite:////tmp/test.db', convert_unicode=True) metadata = MetaData(bind=engine) From 6b9370a8545475c40f84a02e3d49467fb6a83d2e Mon Sep 17 00:00:00 2001 From: EJ Lee Date: Mon, 2 Jun 2014 21:28:27 +0900 Subject: [PATCH 058/731] Update mod_wsgi.rst note configuration changes in apache 2.4 --- docs/deploying/mod_wsgi.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index baac5a1b..dbcfc0f3 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -105,6 +105,10 @@ refuse to run with the above configuration. On a Windows system, eliminate those +Note: There have been some changes in access control configuration for `Apache 2.4`_. + +.. _Apache 2.4: http://httpd.apache.org/docs/trunk/upgrading.html + For more information consult the `mod_wsgi wiki`_. .. _mod_wsgi: http://code.google.com/p/modwsgi/ From 4541910381e5c3836e4c00b480653f058b149f6f Mon Sep 17 00:00:00 2001 From: Jakub Stasiak Date: Wed, 4 Jun 2014 14:47:26 +0100 Subject: [PATCH 059/731] Get rid of _tag->closure->_tag reference cycle --- flask/sessions.py | 50 ++++++++++++++++++++++++----------------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/flask/sessions.py b/flask/sessions.py index 82ba3506..4a7814d7 100644 --- a/flask/sessions.py +++ b/flask/sessions.py @@ -52,36 +52,38 @@ class SessionMixin(object): modified = True +def _tag(value): + if isinstance(value, tuple): + return {' t': [_tag(x) for x in value]} + elif isinstance(value, uuid.UUID): + return {' u': value.hex} + elif isinstance(value, bytes): + return {' b': b64encode(value).decode('ascii')} + elif callable(getattr(value, '__html__', None)): + return {' m': text_type(value.__html__())} + elif isinstance(value, list): + return [_tag(x) for x in value] + elif isinstance(value, datetime): + return {' d': http_date(value)} + elif isinstance(value, dict): + return dict((k, _tag(v)) for k, v in iteritems(value)) + elif isinstance(value, str): + try: + return text_type(value) + except UnicodeError: + raise UnexpectedUnicodeError(u'A byte string with ' + u'non-ASCII data was passed to the session system ' + u'which can only store unicode strings. Consider ' + u'base64 encoding your string (String was %r)' % value) + return value + + class TaggedJSONSerializer(object): """A customized JSON serializer that supports a few extra types that we take for granted when serializing (tuples, markup objects, datetime). """ def dumps(self, value): - def _tag(value): - if isinstance(value, tuple): - return {' t': [_tag(x) for x in value]} - elif isinstance(value, uuid.UUID): - return {' u': value.hex} - elif isinstance(value, bytes): - return {' b': b64encode(value).decode('ascii')} - elif callable(getattr(value, '__html__', None)): - return {' m': text_type(value.__html__())} - elif isinstance(value, list): - return [_tag(x) for x in value] - elif isinstance(value, datetime): - return {' d': http_date(value)} - elif isinstance(value, dict): - return dict((k, _tag(v)) for k, v in iteritems(value)) - elif isinstance(value, str): - try: - return text_type(value) - except UnicodeError: - raise UnexpectedUnicodeError(u'A byte string with ' - u'non-ASCII data was passed to the session system ' - u'which can only store unicode strings. Consider ' - u'base64 encoding your string (String was %r)' % value) - return value return json.dumps(_tag(value), separators=(',', ':')) def loads(self, value): From bf3708609a3a0a2bd03f60e9075ce9e2720156fb Mon Sep 17 00:00:00 2001 From: Shipeng Feng Date: Sun, 15 Jun 2014 12:42:34 +0800 Subject: [PATCH 060/731] Fixed typo in _compat.py --- flask/_compat.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/flask/_compat.py b/flask/_compat.py index d4ec9839..4631d02e 100644 --- a/flask/_compat.py +++ b/flask/_compat.py @@ -19,7 +19,7 @@ _identity = lambda x: x if not PY2: text_type = str string_types = (str,) - integer_types = (int, ) + integer_types = (int,) iterkeys = lambda d: iter(d.keys()) itervalues = lambda d: iter(d.values()) From a8fd417b31f27ffc1704de33dde10ac25c4ea11c Mon Sep 17 00:00:00 2001 From: lord63 Date: Mon, 16 Jun 2014 18:39:16 +0800 Subject: [PATCH 061/731] Update installation.rst Add descriptions about how leave from the virtual environment. --- docs/installation.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/installation.rst b/docs/installation.rst index 163782f2..9169e048 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -81,6 +81,12 @@ If you are a Windows user, the following command is for you:: Either way, you should now be using your virtualenv (notice how the prompt of your shell has changed to show the active environment). +And if you want to go back to the real world, use the following command:: + + $ deactivate + +After doing this, the prompt of your shell should be as familar as before. + Now you can just enter the following command to get Flask activated in your virtualenv:: From c44a6111bc78ace1849748af424ac0ddabbe5f47 Mon Sep 17 00:00:00 2001 From: lord63 Date: Mon, 16 Jun 2014 18:59:33 +0800 Subject: [PATCH 062/731] Update installation.rst --- docs/installation.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/installation.rst b/docs/installation.rst index 9169e048..3923350c 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -87,7 +87,7 @@ And if you want to go back to the real world, use the following command:: After doing this, the prompt of your shell should be as familar as before. -Now you can just enter the following command to get Flask activated in your +Now, let's move on. Enter the following command to get Flask activated in your virtualenv:: $ pip install Flask From 777e23f81e5c496cd89d5789bdd18624e319a7ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wxcaf=C3=A9=20=28Cl=C3=A9ment=20Hertling=29?= Date: Mon, 16 Jun 2014 22:07:20 +0200 Subject: [PATCH 063/731] Update mod_wsgi.rst Changes deprecated pkg_add to pkg install, adds documentation for yum --- docs/deploying/mod_wsgi.rst | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index dbcfc0f3..3c859910 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -29,12 +29,19 @@ follows: # apt-get install libapache2-mod-wsgi +If you are using a yum based distribution (Fedora, OpenSUSE, etc..) you +can install it as follows: + +.. sourcecode:: text + + # yum install mod_wsgi + On FreeBSD install `mod_wsgi` by compiling the `www/mod_wsgi` port or by using pkg_add: .. sourcecode:: text - # pkg_add -r mod_wsgi + # pkg install ap22-mod_wsgi2 If you are using pkgsrc you can install `mod_wsgi` by compiling the `www/ap2-wsgi` package. From 171253a0ebd1262c6e8a03fb682ad93fb523aad8 Mon Sep 17 00:00:00 2001 From: Mark Slater Date: Sun, 29 Jun 2014 15:48:52 -0700 Subject: [PATCH 064/731] Update dbcon.rst "Teared" should be "torn", I think? --- docs/tutorial/dbcon.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst index 0e3f7de5..aa6d8bae 100644 --- a/docs/tutorial/dbcon.rst +++ b/docs/tutorial/dbcon.rst @@ -51,7 +51,7 @@ decorator. It's executed every time the application context tears down:: Functions marked with :meth:`~flask.Flask.teardown_appcontext` are called every time the app context tears down. So what does this mean? Essentially the app context is created before the request comes in and is -destroyed (teared down) whenever the request finishes. A teardown can +destroyed (torn down) whenever the request finishes. A teardown can happen because of two reasons: either everything went well (the error parameter will be `None`) or an exception happened in which case the error is passed to the teardown function. From 35282a881eeb11be0e3b4e39ce3e1b6671eb2055 Mon Sep 17 00:00:00 2001 From: Mark Slater Date: Sun, 29 Jun 2014 15:51:03 -0700 Subject: [PATCH 065/731] Update dbinit.rst "require" -> "requires" --- docs/tutorial/dbinit.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/dbinit.rst b/docs/tutorial/dbinit.rst index 10404b3a..e3dc60fb 100644 --- a/docs/tutorial/dbinit.rst +++ b/docs/tutorial/dbinit.rst @@ -16,7 +16,7 @@ Such a schema can be created by piping the `schema.sql` file into the The downside of this is that it requires the sqlite3 command to be installed which is not necessarily the case on every system. This also -require that we provide the path to the database which can introduce +requires that we provide the path to the database which can introduce errors. It's a good idea to add a function that initializes the database for you to the application. From 2d992e7f135f6e3aa521ad210fc0febd5b93d30f Mon Sep 17 00:00:00 2001 From: root-11 Date: Tue, 1 Jul 2014 15:57:50 +0100 Subject: [PATCH 066/731] Update schema.rst The error is that sqlite3 needs escaping on the database column 'text' in the line: text text not null so that it becomes: 'text' text not null The example on mitsuhiko/flask is correct and helped me detecting the mistake. --- docs/tutorial/schema.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorial/schema.rst b/docs/tutorial/schema.rst index f8455037..246baccd 100644 --- a/docs/tutorial/schema.rst +++ b/docs/tutorial/schema.rst @@ -14,7 +14,7 @@ named `schema.sql` in the just created `flaskr` folder: create table entries ( id integer primary key autoincrement, title text not null, - text text not null + 'text' text not null ); This schema consists of a single table called `entries` and each row in From eb0c8a6beea766812fb94712c27ed6e1e99d6249 Mon Sep 17 00:00:00 2001 From: Suraj Patil Date: Thu, 3 Jul 2014 18:19:09 +0530 Subject: [PATCH 067/731] The example does not execute With the inclusion of the app.run we now are able to execute the program, it would be great if we'd have even more detailed docs available for the examples --- examples/blueprintexample/blueprintexample.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/examples/blueprintexample/blueprintexample.py b/examples/blueprintexample/blueprintexample.py index 925f4845..78ee3a5b 100644 --- a/examples/blueprintexample/blueprintexample.py +++ b/examples/blueprintexample/blueprintexample.py @@ -5,3 +5,6 @@ app = Flask(__name__) app.register_blueprint(simple_page) # Blueprint can be registered many times app.register_blueprint(simple_page, url_prefix='/pages') + +if __name__=='__main__': + app.run() From f5b15c2a35933167c516da0c13290375c58c5509 Mon Sep 17 00:00:00 2001 From: Jihyeok Seo Date: Sun, 6 Jul 2014 00:32:31 +0900 Subject: [PATCH 068/731] Fix typo in cli docs --- docs/cli.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/cli.rst b/docs/cli.rst index f1d8976d..6eb6cb9c 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -194,7 +194,7 @@ step. either directly import an application object or create it (see :ref:`app-factories`). - What is ``data.info``? It's a dictionary of arbitrary data on the + What is ``info.data``? It's a dictionary of arbitrary data on the script info that can be filled by options or through other means. We will come back to this later. 3. Next step is to create a :class:`FlaskGroup`. In this case we just From add59a91cf45b567397db134d3e4ed4ce3ecd6da Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Mon, 7 Jul 2014 21:17:20 +0900 Subject: [PATCH 069/731] Stop recommending Python 2.6 --- docs/advanced_foreword.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/advanced_foreword.rst b/docs/advanced_foreword.rst index 9f7a73bd..22e333f8 100644 --- a/docs/advanced_foreword.rst +++ b/docs/advanced_foreword.rst @@ -57,7 +57,7 @@ partially caused by changes in the language that went unreviewed for too long, partially also because we have not quite worked out how the lower- level API should change to account for the Unicode differences in Python 3. -We strongly recommend using Python 2.6 and 2.7 with activated Python 3 +We strongly recommend using Python 2.7 with activated Python 3 warnings during development. If you plan on upgrading to Python 3 in the near future we strongly recommend that you read `How to write forwards compatible Python code From 931be87475634c79deae584b7fa8d2bd6d6c55c0 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Thu, 10 Jul 2014 23:01:32 +0900 Subject: [PATCH 070/731] Update python3.rst Fixes #1101 --- docs/python3.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/docs/python3.rst b/docs/python3.rst index 8173dd45..13cc8455 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -40,12 +40,11 @@ probably hard to search for on the internet if they are Python 3 specific. Small Ecosystem --------------- -The majority of the Flask extensions, all of the documentation and the -vast majority of the PyPI provided libraries do not support Python 3 yet. -Even if you start your project with knowing that all you will need is -supported by Python 3 you don't know what happens six months from now. If -you are adventurous you can start porting libraries on your own, but that -is nothing for the faint of heart. +Some Flask extensions, documentation and PyPI provided libraries do not +support Python 3 yet. Even if you start your project with knowing that +all you will need is supported by Python 3 you don't know what happens six +months from now. If you are adventurous you can start porting libraries +on your own, but that is nothing for the faint of heart. Recommendations --------------- From 92191fb9d9f9d68811be81977faf108d9f1313f4 Mon Sep 17 00:00:00 2001 From: lord63 Date: Wed, 9 Jul 2014 13:22:27 +0800 Subject: [PATCH 071/731] Fix a typo in templating.rst --- docs/templating.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/templating.rst b/docs/templating.rst index bf672672..8b7caf44 100644 --- a/docs/templating.rst +++ b/docs/templating.rst @@ -119,7 +119,7 @@ Controlling Autoescaping ------------------------ Autoescaping is the concept of automatically escaping special characters -of you. Special characters in the sense of HTML (or XML, and thus XHTML) +for you. Special characters in the sense of HTML (or XML, and thus XHTML) are ``&``, ``>``, ``<``, ``"`` as well as ``'``. Because these characters carry specific meanings in documents on their own you have to replace them by so called "entities" if you want to use them for text. Not doing so From 3e9f074d05c732c65cad104235807ed662a7df16 Mon Sep 17 00:00:00 2001 From: lord63 Date: Wed, 9 Jul 2014 14:22:30 +0800 Subject: [PATCH 072/731] Update becomingbig.rst --- docs/becomingbig.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/becomingbig.rst b/docs/becomingbig.rst index 62f456bd..8b0a2743 100644 --- a/docs/becomingbig.rst +++ b/docs/becomingbig.rst @@ -12,7 +12,7 @@ Flask started in part to demonstrate how to build your own framework on top of existing well-used tools Werkzeug (WSGI) and Jinja (templating), and as it developed, it became useful to a wide audience. As you grow your codebase, don't just use Flask -- understand it. Read the source. Flask's code is -written to be read; it's documentation published so you can use its internal +written to be read; it's documentation is published so you can use its internal APIs. Flask sticks to documented APIs in upstream libraries, and documents its internal utilities so that you can find the hook points needed for your project. From de5bb759e3b5f686769c0803541a555475b3ef5f Mon Sep 17 00:00:00 2001 From: lord63 Date: Wed, 9 Jul 2014 14:45:55 +0800 Subject: [PATCH 073/731] Fix typos in shell.rst --- docs/shell.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/shell.rst b/docs/shell.rst index f4522c1a..55a428c7 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -94,10 +94,10 @@ Further Improving the Shell Experience -------------------------------------- If you like the idea of experimenting in a shell, create yourself a module -with stuff you want to star import into your interactive session. There +with stuff you want to firstly import into your interactive session. There you could also define some more helper methods for common things such as initializing the database, dropping tables etc. -Just put them into a module (like `shelltools` and import from there): +Just put them into a module (like `shelltools`) and import from there: >>> from shelltools import * From 7777b06809a4fd948e86f5ce8681c8f401ff37ee Mon Sep 17 00:00:00 2001 From: lord63 Date: Thu, 10 Jul 2014 12:04:48 +0800 Subject: [PATCH 074/731] Fix a typo in config.rst --- docs/config.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/config.rst b/docs/config.rst index d0e1bcdb..e5b2369b 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -7,7 +7,7 @@ Configuration Handling Applications need some kind of configuration. There are different settings you might want to change depending on the application environment like -toggling the debug mode, setting the secret key, and other such +toggling the debug mode, setting the secret key, and other such as environment-specific things. The way Flask is designed usually requires the configuration to be From 02e7bec21cec2f32d954e178a1a88f8dd6aaa81a Mon Sep 17 00:00:00 2001 From: lord63 Date: Fri, 11 Jul 2014 23:23:01 +0800 Subject: [PATCH 075/731] Fix typos in config.rst --- docs/config.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index e5b2369b..6bece190 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -10,7 +10,7 @@ you might want to change depending on the application environment like toggling the debug mode, setting the secret key, and other such as environment-specific things. -The way Flask is designed usually requires the configuration to be +The way Flask designed usually requires the configuration to be available when the application starts up. You can hardcode the configuration in the code, which for many small applications is not actually that bad, but there are better ways. @@ -194,7 +194,7 @@ The following configuration values are used internally by Flask: browsers will not allow cross-subdomain cookies to be set on a server name without dots in it. So if your server name is ``'localhost'`` you will not be able to set a cookie for - ``'localhost'`` and every subdomain of it. Please chose a different + ``'localhost'`` and every subdomain of it. Please choose a different server name in that case, like ``'myapplication.local'`` and add this name + the subdomains you want to use into your host config or setup a local `bind`_. @@ -249,7 +249,7 @@ So a common pattern is this:: This first loads the configuration from the `yourapplication.default_settings` module and then overrides the values with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS` -environment variable points to. This environment variable can be set on +environment variable pointing to. This environment variable can be set on Linux or OS X with the export command in the shell before starting the server:: @@ -287,7 +287,7 @@ a little harder. There is no single 100% solution for this problem in general, but there are a couple of things you can keep in mind to improve that experience: -1. create your application in a function and register blueprints on it. +1. Create your application in a function and register blueprints on it. That way you can create multiple instances of your application with different configurations attached which makes unittesting a lot easier. You can use this to pass in configuration as needed. @@ -348,10 +348,10 @@ To enable such a config you just have to call into There are many different ways and it's up to you how you want to manage your configuration files. However here a list of good recommendations: -- keep a default configuration in version control. Either populate the +- Keep a default configuration in version control. Either populate the config with this default configuration or import it in your own configuration files before overriding values. -- use an environment variable to switch between the configurations. +- Use an environment variable to switch between the configurations. This can be done from outside the Python interpreter and makes development and deployment much easier because you can quickly and easily switch between different configs without having to touch the From c8ce5326b4d30f29aaadbb3b81bdcb1cec6b43e8 Mon Sep 17 00:00:00 2001 From: Miles Richardson Date: Tue, 15 Jul 2014 15:19:00 +0800 Subject: [PATCH 076/731] fixed typo -- missing "are" in docs/signals.rst --- docs/signals.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/signals.rst b/docs/signals.rst index 46342033..c32a5ca2 100644 --- a/docs/signals.rst +++ b/docs/signals.rst @@ -148,7 +148,7 @@ signal subscribers:: model_saved.send(self) Try to always pick a good sender. If you have a class that is emitting a -signal, pass `self` as sender. If you emitting a signal from a random +signal, pass `self` as sender. If you are emitting a signal from a random function, you can pass ``current_app._get_current_object()`` as sender. .. admonition:: Passing Proxies as Senders From 22219f51e5a15836657bc5553fe90dac2054caef Mon Sep 17 00:00:00 2001 From: Nico Revin Date: Thu, 24 Jul 2014 19:03:56 +0400 Subject: [PATCH 077/731] Update doclinks Fix redirects and broken links --- docs/config.rst | 4 ++-- docs/deploying/cgi.rst | 4 ++-- docs/deploying/fastcgi.rst | 6 +++--- docs/deploying/mod_wsgi.rst | 4 ++-- docs/deploying/uwsgi.rst | 2 +- docs/extensions.rst | 2 +- docs/htmlfaq.rst | 2 +- docs/installation.rst | 4 ++-- docs/patterns/caching.rst | 4 ++-- docs/patterns/fabric.rst | 4 ++-- docs/patterns/jquery.rst | 2 +- docs/patterns/sqlalchemy.rst | 6 +++--- docs/patterns/wtforms.rst | 4 ++-- docs/quickstart.rst | 4 ++-- docs/testing.rst | 36 ++++++++++++++++++------------------ docs/tutorial/dbcon.rst | 2 +- docs/tutorial/index.rst | 2 +- docs/upgrading.rst | 4 ++-- 18 files changed, 48 insertions(+), 48 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index d0e1bcdb..dfcead34 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -199,7 +199,7 @@ The following configuration values are used internally by Flask: this name + the subdomains you want to use into your host config or setup a local `bind`_. -.. _bind: https://www.isc.org/software/bind +.. _bind: https://www.isc.org/downloads/bind/ .. versionadded:: 0.4 ``LOGGER_NAME`` @@ -363,7 +363,7 @@ your configuration files. However here a list of good recommendations: details about how to do that, head over to the :ref:`fabric-deployment` pattern. -.. _fabric: http://fabfile.org/ +.. _fabric: http://www.fabfile.org/ .. _instance-folders: diff --git a/docs/deploying/cgi.rst b/docs/deploying/cgi.rst index 3225bc58..9675d673 100644 --- a/docs/deploying/cgi.rst +++ b/docs/deploying/cgi.rst @@ -51,11 +51,11 @@ your app to be available, works too but the `ScriptAlias` directive won't work in that case: .. sourcecode:: apache - + RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f # Don't interfere with static files RewriteRule ^(.*)$ /path/to/the/application.cgi/$1 [L] For more information consult the documentation of your webserver. -.. _App Engine: http://code.google.com/appengine/ +.. _App Engine: https://developers.google.com/appengine/ diff --git a/docs/deploying/fastcgi.rst b/docs/deploying/fastcgi.rst index b4f01d57..a0d6d076 100644 --- a/docs/deploying/fastcgi.rst +++ b/docs/deploying/fastcgi.rst @@ -159,7 +159,7 @@ work in the URL root you have to work around a lighttpd bug with the Make sure to apply it only if you are mounting the application the URL root. Also, see the Lighty docs for more information on `FastCGI and Python -`_ (note that +`_ (note that explicitly passing a socket to run() is no longer necessary). Configuring nginx @@ -236,5 +236,5 @@ python path. Common problems are: .. _nginx: http://nginx.org/ .. _lighttpd: http://www.lighttpd.net/ -.. _cherokee: http://www.cherokee-project.com/ -.. _flup: http://trac.saddi.com/flup +.. _cherokee: http://cherokee-project.com/ +.. _flup: https://pypi.python.org/pypi/flup diff --git a/docs/deploying/mod_wsgi.rst b/docs/deploying/mod_wsgi.rst index 3c859910..e91aedc9 100644 --- a/docs/deploying/mod_wsgi.rst +++ b/docs/deploying/mod_wsgi.rst @@ -98,7 +98,7 @@ execute the application under a different user for security reasons: -Note: WSGIDaemonProcess isn't implemented in Windows and Apache will +Note: WSGIDaemonProcess isn't implemented in Windows and Apache will refuse to run with the above configuration. On a Windows system, eliminate those lines: .. sourcecode:: apache @@ -121,7 +121,7 @@ For more information consult the `mod_wsgi wiki`_. .. _mod_wsgi: http://code.google.com/p/modwsgi/ .. _installation instructions: http://code.google.com/p/modwsgi/wiki/QuickInstallationGuide .. _virtual python: https://pypi.python.org/pypi/virtualenv -.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/wiki/ +.. _mod_wsgi wiki: http://code.google.com/p/modwsgi/w/list Troubleshooting --------------- diff --git a/docs/deploying/uwsgi.rst b/docs/deploying/uwsgi.rst index b05fdeec..25ed8008 100644 --- a/docs/deploying/uwsgi.rst +++ b/docs/deploying/uwsgi.rst @@ -63,5 +63,5 @@ it the WSGI `SCRIPT_NAME` or set the uwsgi modifier to make use of it:: .. _nginx: http://nginx.org/ .. _lighttpd: http://www.lighttpd.net/ -.. _cherokee: http://www.cherokee-project.com/ +.. _cherokee: http://cherokee-project.com/ .. _uwsgi: http://projects.unbit.it/uwsgi/ diff --git a/docs/extensions.rst b/docs/extensions.rst index 53dca56e..4f80f1e6 100644 --- a/docs/extensions.rst +++ b/docs/extensions.rst @@ -45,4 +45,4 @@ Once the ``flaskext_compat`` module is activated the :data:`flask.ext` will exist and you can start importing from there. .. _Flask Extension Registry: http://flask.pocoo.org/extensions/ -.. _flaskext_compat.py: https://github.com/mitsuhiko/flask/raw/master/scripts/flaskext_compat.py +.. _flaskext_compat.py: https://raw.githubusercontent.com/mitsuhiko/flask/master/scripts/flaskext_compat.py diff --git a/docs/htmlfaq.rst b/docs/htmlfaq.rst index 434bb656..fdf29634 100644 --- a/docs/htmlfaq.rst +++ b/docs/htmlfaq.rst @@ -186,7 +186,7 @@ Many other features have been added, as well. A good guide to new features in HTML5 is Mark Pilgrim's soon-to-be-published book, `Dive Into HTML5`_. Not all of them are supported in browsers yet, however, so use caution. -.. _Dive Into HTML5: http://www.diveintohtml5.info/ +.. _Dive Into HTML5: http://diveintohtml5.info/ What should be used? -------------------- diff --git a/docs/installation.rst b/docs/installation.rst index 3923350c..5bf668af 100644 --- a/docs/installation.rst +++ b/docs/installation.rst @@ -180,8 +180,8 @@ check that you can now just type ``python`` to bring up the interpreter. Finally, to install `virtualenv`_, you can simply run:: > pip install virtualenv - + Then you can be off on your way following the installation instructions above. -.. _get-pip.py: https://raw.github.com/pypa/pip/master/contrib/get-pip.py +.. _get-pip.py: https://raw.githubusercontent.com/pypa/pip/master/contrib/get-pip.py .. _ez_setup.py: https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py diff --git a/docs/patterns/caching.rst b/docs/patterns/caching.rst index a0633cf9..97e0f35d 100644 --- a/docs/patterns/caching.rst +++ b/docs/patterns/caching.rst @@ -27,7 +27,7 @@ cache that keeps the item stored in the memory of the Python interpreter:: cache = SimpleCache() If you want to use memcached, make sure to have one of the memcache modules -supported (you get them from `PyPI `_) and a +supported (you get them from `PyPI `_) and a memcached server running somewhere. This is how you connect to such an memcached server then:: @@ -44,7 +44,7 @@ Using a Cache ------------- Now how can one use such a cache? There are two very important -operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and +operations: :meth:`~werkzeug.contrib.cache.BaseCache.get` and :meth:`~werkzeug.contrib.cache.BaseCache.set`. This is how to use them: To get an item from the cache call diff --git a/docs/patterns/fabric.rst b/docs/patterns/fabric.rst index b02ad277..e915103b 100644 --- a/docs/patterns/fabric.rst +++ b/docs/patterns/fabric.rst @@ -186,11 +186,11 @@ deployment actually fun: out the latest version on the server and then install. That way you can also easily go back to older versions. - hook in testing functionality so that you can deploy to an external - server and run the testsuite. + server and run the testsuite. Working with Fabric is fun and you will notice that it's quite magical to type ``fab deploy`` and see your application being deployed automatically to one or more remote servers. -.. _Fabric: http://fabfile.org/ +.. _Fabric: http://www.fabfile.org/ diff --git a/docs/patterns/jquery.rst b/docs/patterns/jquery.rst index bb1b4c06..913bcedf 100644 --- a/docs/patterns/jquery.rst +++ b/docs/patterns/jquery.rst @@ -164,5 +164,5 @@ explanation of the little bit of code above: If you don't get the whole picture, download the `sourcecode for this example -`_ +`_ from github. diff --git a/docs/patterns/sqlalchemy.rst b/docs/patterns/sqlalchemy.rst index 29a0d9f9..d34f39be 100644 --- a/docs/patterns/sqlalchemy.rst +++ b/docs/patterns/sqlalchemy.rst @@ -110,7 +110,7 @@ Querying is simple as well: .. _SQLAlchemy: http://www.sqlalchemy.org/ .. _declarative: - http://www.sqlalchemy.org/docs/orm/extensions/declarative.html + http://docs.sqlalchemy.org/en/latest/orm/extensions/declarative.html Manual Object Relational Mapping -------------------------------- @@ -186,7 +186,7 @@ Then you can either declare the tables in your code like in the examples above, or automatically load them:: from sqlalchemy import Table - + users = Table('users', metadata, autoload=True) To insert data you can use the `insert` method. We have to get a @@ -215,4 +215,4 @@ You can also pass strings of SQL statements to the (1, u'admin', u'admin@localhost') For more information about SQLAlchemy, head over to the -`website `_. +`website `_. diff --git a/docs/patterns/wtforms.rst b/docs/patterns/wtforms.rst index 8017aea5..49bdbd43 100644 --- a/docs/patterns/wtforms.rst +++ b/docs/patterns/wtforms.rst @@ -122,5 +122,5 @@ takes advantage of the `_formhelpers.html` template: For more information about WTForms, head over to the `WTForms website`_. -.. _WTForms: http://wtforms.simplecodes.com/ -.. _WTForms website: http://wtforms.simplecodes.com/ +.. _WTForms: http://wtforms.readthedocs.org/ +.. _WTForms website: http://wtforms.readthedocs.org/ diff --git a/docs/quickstart.rst b/docs/quickstart.rst index b39b2a3c..accb942f 100644 --- a/docs/quickstart.rst +++ b/docs/quickstart.rst @@ -41,7 +41,7 @@ should see your hello world greeting. So what did that code do? 1. First we imported the :class:`~flask.Flask` class. An instance of this - class will be our WSGI application. + class will be our WSGI application. 2. Next we create an instance of this class. The first argument is the name of the application's module or package. If you are using a single module (as in this example), you should use `__name__` because depending on if it's @@ -878,7 +878,7 @@ Ready to deploy your new Flask app? To wrap up the quickstart, you can immediately deploy to a hosted platform, all of which offer a free plan for small projects: -- `Deploying Flask on Heroku `_ +- `Deploying Flask on Heroku `_ - `Deploying WSGI on dotCloud `_ with `Flask-specific notes `_ diff --git a/docs/testing.rst b/docs/testing.rst index 95b2021a..de5c9143 100644 --- a/docs/testing.rst +++ b/docs/testing.rst @@ -11,7 +11,7 @@ improve existing code and developers of untested applications tend to become pretty paranoid. If an application has automated tests, you can safely make changes and instantly know if anything breaks. -Flask provides a way to test your application by exposing the Werkzeug +Flask provides a way to test your application by exposing the Werkzeug test :class:`~werkzeug.test.Client` and handling the context locals for you. You can then use that with your favourite testing solution. In this documentation we will use the :mod:`unittest` package that comes pre-installed with Python. @@ -19,17 +19,17 @@ we will use the :mod:`unittest` package that comes pre-installed with Python. The Application --------------- -First, we need an application to test; we will use the application from -the :ref:`tutorial`. If you don't have that application yet, get the +First, we need an application to test; we will use the application from +the :ref:`tutorial`. If you don't have that application yet, get the sources from `the examples`_. .. _the examples: - http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ + https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ The Testing Skeleton -------------------- -In order to test the application, we add a second module +In order to test the application, we add a second module (`flaskr_tests.py`) and create a unittest skeleton there:: import os @@ -55,15 +55,15 @@ In order to test the application, we add a second module The code in the :meth:`~unittest.TestCase.setUp` method creates a new test client and initializes a new database. This function is called before -each individual test function is run. To delete the database after the +each individual test function is run. To delete the database after the test, we close the file and remove it from the filesystem in the :meth:`~unittest.TestCase.tearDown` method. Additionally during setup the ``TESTING`` config flag is activated. What it does is disabling the error catching during request handling so that you get better error reports when performing test requests against the application. -This test client will give us a simple interface to the application. We can -trigger test requests to the application, and the client will also keep track +This test client will give us a simple interface to the application. We can +trigger test requests to the application, and the client will also keep track of cookies for us. Because SQLite3 is filesystem-based we can easily use the tempfile module @@ -89,8 +89,8 @@ with an exception. The First Test -------------- -Now it's time to start testing the functionality of the application. -Let's check that the application shows "No entries here so far" if we +Now it's time to start testing the functionality of the application. +Let's check that the application shows "No entries here so far" if we access the root of the application (``/``). To do this, we add a new test method to our class, like this:: @@ -109,13 +109,13 @@ test method to our class, like this:: rv = self.app.get('/') assert 'No entries here so far' in rv.data -Notice that our test functions begin with the word `test`; this allows -:mod:`unittest` to automatically identify the method as a test to run. +Notice that our test functions begin with the word `test`; this allows +:mod:`unittest` to automatically identify the method as a test to run. -By using `self.app.get` we can send an HTTP `GET` request to the application with -the given path. The return value will be a :class:`~flask.Flask.response_class` object. +By using `self.app.get` we can send an HTTP `GET` request to the application with +the given path. The return value will be a :class:`~flask.Flask.response_class` object. We can now use the :attr:`~werkzeug.wrappers.BaseResponse.data` attribute to inspect -the return value (as string) from the application. In this case, we ensure that +the return value (as string) from the application. In this case, we ensure that ``'No entries here so far'`` is part of the output. Run it again and you should see one passing test:: @@ -132,8 +132,8 @@ Logging In and Out The majority of the functionality of our application is only available for the administrative user, so we need a way to log our test client in and out -of the application. To do this, we fire some requests to the login and logout -pages with the required form data (username and password). And because the +of the application. To do this, we fire some requests to the login and logout +pages with the required form data (username and password). And because the login and logout pages redirect, we tell the client to `follow_redirects`. Add the following two methods to your `FlaskrTestCase` class:: @@ -194,7 +194,7 @@ suite. .. _MiniTwit Example: - http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/ + https://github.com/mitsuhiko/flask/tree/master/examples/minitwit/ Other Testing Tricks diff --git a/docs/tutorial/dbcon.rst b/docs/tutorial/dbcon.rst index aa6d8bae..00f6c32e 100644 --- a/docs/tutorial/dbcon.rst +++ b/docs/tutorial/dbcon.rst @@ -75,4 +75,4 @@ Continue to :ref:`tutorial-dbinit`. larger `, it's a good idea not to. .. _example source: - http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ + https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ diff --git a/docs/tutorial/index.rst b/docs/tutorial/index.rst index da37cf7a..beb22709 100644 --- a/docs/tutorial/index.rst +++ b/docs/tutorial/index.rst @@ -15,7 +15,7 @@ If you want the full sourcecode in advance or for comparison, check out the `example source`_. .. _example source: - http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ + https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/ .. toctree:: :maxdepth: 2 diff --git a/docs/upgrading.rst b/docs/upgrading.rst index ccfa82ad..5b2d6f85 100644 --- a/docs/upgrading.rst +++ b/docs/upgrading.rst @@ -64,7 +64,7 @@ If you maintain an extension that was using :data:`~flask._request_ctx_stack` before, please consider changing to :data:`~flask._app_ctx_stack` if it makes sense for your extension. For instance, the app context stack makes sense for extensions which connect to databases. Using the app context stack instead of -the request context stack will make extensions more readily handle use cases +the request context stack will make extensions more readily handle use cases outside of requests. Version 0.8 @@ -115,7 +115,7 @@ good. To apply the upgrade script do the following: 1. Download the script: `flask-07-upgrade.py - `_ + `_ 2. Run it in the directory of your application:: python flask-07-upgrade.py > patchfile.diff From e429e47a967e6846fa6ef723364dd25cc9aea37b Mon Sep 17 00:00:00 2001 From: lord63 Date: Sat, 26 Jul 2014 20:15:36 +0800 Subject: [PATCH 078/731] Correct the mistakes. --- docs/config.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/config.rst b/docs/config.rst index 6bece190..660f6ca5 100644 --- a/docs/config.rst +++ b/docs/config.rst @@ -7,10 +7,10 @@ Configuration Handling Applications need some kind of configuration. There are different settings you might want to change depending on the application environment like -toggling the debug mode, setting the secret key, and other such as +toggling the debug mode, setting the secret key, and other such environment-specific things. -The way Flask designed usually requires the configuration to be +The way Flask is designed usually requires the configuration to be available when the application starts up. You can hardcode the configuration in the code, which for many small applications is not actually that bad, but there are better ways. @@ -249,7 +249,7 @@ So a common pattern is this:: This first loads the configuration from the `yourapplication.default_settings` module and then overrides the values with the contents of the file the :envvar:`YOURAPPLICATION_SETTINGS` -environment variable pointing to. This environment variable can be set on +environment variable points to. This environment variable can be set on Linux or OS X with the export command in the shell before starting the server:: From efdf2e12125f52f02a68e20b6df1814635d2c005 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sat, 26 Jul 2014 21:47:41 +0900 Subject: [PATCH 079/731] Update python3.rst --- docs/python3.rst | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/python3.rst b/docs/python3.rst index 13cc8455..a06a270f 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -33,18 +33,20 @@ guarantee that this won't happen on Python 3. Few Users --------- -Python 3 currently has less than 1% of the users of Python 2 going by PyPI -download stats. As a result many of the problems you will encounter are +Although moving to Python 3 should be done someday, most people still uses +Python 2 for now. As a result many of the problems you will encounter are probably hard to search for on the internet if they are Python 3 specific. Small Ecosystem --------------- Some Flask extensions, documentation and PyPI provided libraries do not -support Python 3 yet. Even if you start your project with knowing that -all you will need is supported by Python 3 you don't know what happens six -months from now. If you are adventurous you can start porting libraries -on your own, but that is nothing for the faint of heart. +support Python 3 yet. + +Even if you start your project with knowing that all you will need is +supported by Python 3 you don't know what happens six months from now. +But if you are familiar Python 3 and Flask extension, you can start porting +libraries on your own. Recommendations --------------- From 29768ce45e6e46fd830d11f17250250fd8c5c5fb Mon Sep 17 00:00:00 2001 From: lord63 Date: Sat, 26 Jul 2014 22:43:52 +0800 Subject: [PATCH 080/731] Correct the mistake again. --- docs/shell.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/shell.rst b/docs/shell.rst index 55a428c7..f622be9d 100644 --- a/docs/shell.rst +++ b/docs/shell.rst @@ -94,7 +94,7 @@ Further Improving the Shell Experience -------------------------------------- If you like the idea of experimenting in a shell, create yourself a module -with stuff you want to firstly import into your interactive session. There +with stuff you want to star import into your interactive session. There you could also define some more helper methods for common things such as initializing the database, dropping tables etc. From a61347b902961d36d932bab56794fc39fab0d210 Mon Sep 17 00:00:00 2001 From: INADA Naoki Date: Sun, 27 Jul 2014 02:47:33 +0900 Subject: [PATCH 081/731] Update python3.rst --- docs/python3.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/python3.rst b/docs/python3.rst index a06a270f..34fec4e9 100644 --- a/docs/python3.rst +++ b/docs/python3.rst @@ -33,7 +33,7 @@ guarantee that this won't happen on Python 3. Few Users --------- -Although moving to Python 3 should be done someday, most people still uses +Although moving to Python 3 should be done someday, most people still use Python 2 for now. As a result many of the problems you will encounter are probably hard to search for on the internet if they are Python 3 specific. @@ -45,8 +45,8 @@ support Python 3 yet. Even if you start your project with knowing that all you will need is supported by Python 3 you don't know what happens six months from now. -But if you are familiar Python 3 and Flask extension, you can start porting -libraries on your own. +But if you are familiar with Python 3 and Flask extension, you can start +porting libraries on your own. Recommendations --------------- From aa40b1731e09e839502994b3b0c22d3f2361d045 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sun, 27 Jul 2014 11:19:52 +0200 Subject: [PATCH 082/731] Add Config.from_mapping --- CHANGES | 1 + flask/config.py | 22 ++++++++++++++++++++++ flask/testsuite/config.py | 28 ++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/CHANGES b/CHANGES index 93364231..ec031c53 100644 --- a/CHANGES +++ b/CHANGES @@ -36,6 +36,7 @@ Version 1.0 for an extension author to create exceptions that will by default result in the HTTP error of their choosing, but may be caught with a custom error handler if desired. +- Added :meth:`flask.Config.from_mapping`. Version 0.10.2 -------------- diff --git a/flask/config.py b/flask/config.py index dfc2f6b3..6119f02b 100644 --- a/flask/config.py +++ b/flask/config.py @@ -193,6 +193,28 @@ class Config(dict): self[key] = obj[key] return True + def from_mapping(self, *mapping, **kwargs): + """Updates the config like :meth:`update` ignoring items with non-upper + keys. + + .. versionadded:: 1.0 + """ + mappings = [] + if len(mapping) == 1: + if hasattr(mapping[0], 'items'): + mappings.append(mapping[0].items()) + else: + mappings.append(mapping[0]) + elif len(mapping) > 1: + raise TypeError( + 'expected at most 1 positional argument, got %d' % len(mapping) + ) + mappings.append(kwargs.items()) + for mapping in mappings: + for (key, value) in mapping: + if key.isupper(): + self[key] = value + def get_namespace(self, namespace, lowercase=True): """Returns a dictionary containing a subset of configuration options that match the specified namespace/prefix. Example usage:: diff --git a/flask/testsuite/config.py b/flask/testsuite/config.py index d0542200..4772fa77 100644 --- a/flask/testsuite/config.py +++ b/flask/testsuite/config.py @@ -47,6 +47,34 @@ class ConfigTestCase(FlaskTestCase): app.config.from_json(os.path.join(current_dir, 'static', 'config.json')) self.common_object_test(app) + def test_config_from_mapping(self): + app = flask.Flask(__name__) + app.config.from_mapping({ + 'SECRET_KEY': 'devkey', + 'TEST_KEY': 'foo' + }) + self.common_object_test(app) + + app = flask.Flask(__name__) + app.config.from_mapping([ + ('SECRET_KEY', 'devkey'), + ('TEST_KEY', 'foo') + ]) + self.common_object_test(app) + + app = flask.Flask(__name__) + app.config.from_mapping( + SECRET_KEY='devkey', + TEST_KEY='foo' + ) + self.common_object_test(app) + + app = flask.Flask(__name__) + with self.assert_raises(TypeError): + app.config.from_mapping( + {}, {} + ) + def test_config_from_class(self): class Base(object): TEST_KEY = 'foo' From 486c1089b0ce968b74e81649cdceb0e158157036 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Neuha=CC=88user?= Date: Sun, 27 Jul 2014 13:21:14 +0200 Subject: [PATCH 083/731] Base from_json on from_mapping --- flask/config.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/flask/config.py b/flask/config.py index 6119f02b..bb32f7a7 100644 --- a/flask/config.py +++ b/flask/config.py @@ -167,8 +167,8 @@ class Config(dict): def from_json(self, filename, silent=False): """Updates the values in the config from a JSON file. This function - behaves as if the JSON object was a dictionary and passed ot the - :meth:`from_object` function. + behaves as if the JSON object was a dictionary and passed to the + :meth:`from_mapping` function. :param filename: the filename of the JSON file. This can either be an absolute filename or a filename relative to the @@ -188,10 +188,7 @@ class Config(dict): return False e.strerror = 'Unable to load configuration file (%s)' % e.strerror raise - for key in obj.keys(): - if key.isupper(): - self[key] = obj[key] - return True + return self.from_mapping(obj) def from_mapping(self, *mapping, **kwargs): """Updates the config like :meth:`update` ignoring items with non-upper @@ -214,6 +211,7 @@ class Config(dict): for (key, value) in mapping: if key.isupper(): self[key] = value + return True def get_namespace(self, namespace, lowercase=True): """Returns a dictionary containing a subset of configuration options From 3c48bf893590fe87445a735584591201c3085e35 Mon Sep 17 00:00:00 2001 From: Jeffrey D Date: Tue, 29 Jul 2014 20:42:28 -0500 Subject: [PATCH 084/731] Addressed issue #1134 --- .../simple_page/templates/pages/layout.html | 2 +- examples/flaskr/templates/layout.html | 8 ++++---- examples/flaskr/templates/login.html | 10 +++++----- examples/flaskr/templates/show_entries.html | 10 +++++----- examples/jqueryexample/templates/index.html | 10 +++++----- examples/jqueryexample/templates/layout.html | 4 ++-- examples/minitwit/templates/layout.html | 12 ++++++------ examples/minitwit/templates/login.html | 10 +++++----- examples/minitwit/templates/register.html | 14 +++++++------- examples/minitwit/templates/timeline.html | 16 ++++++++-------- examples/persona/templates/layout.html | 8 ++++---- 11 files changed, 52 insertions(+), 52 deletions(-) diff --git a/examples/blueprintexample/simple_page/templates/pages/layout.html b/examples/blueprintexample/simple_page/templates/pages/layout.html index e74a5871..5eaa8fc6 100644 --- a/examples/blueprintexample/simple_page/templates/pages/layout.html +++ b/examples/blueprintexample/simple_page/templates/pages/layout.html @@ -1,6 +1,6 @@ Simple Page Blueprint -
+

This is blueprint example

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

+ +

Flaskr

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

Login

- {% if error %}

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

+ {% if error %}

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

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

      {{ entry.text|safe }} {% else %} diff --git a/examples/jqueryexample/templates/index.html b/examples/jqueryexample/templates/index.html index 55d01631..b6118cf4 100644 --- a/examples/jqueryexample/templates/index.html +++ b/examples/jqueryexample/templates/index.html @@ -1,6 +1,6 @@ {% extends "layout.html" %} {% block body %} -

      jQuery Example

      - + - = - ? -

      calculate server side + + + = + ? +

      calculate server side {% endblock %} diff --git a/examples/jqueryexample/templates/layout.html b/examples/jqueryexample/templates/layout.html index 3e2ed69b..8be7606e 100644 --- a/examples/jqueryexample/templates/layout.html +++ b/examples/jqueryexample/templates/layout.html @@ -1,8 +1,8 @@ jQuery Example - - {% block body %}{% endblock %} diff --git a/examples/minitwit/templates/layout.html b/examples/minitwit/templates/layout.html index 668e3895..5a43df61 100644 --- a/examples/minitwit/templates/layout.html +++ b/examples/minitwit/templates/layout.html @@ -1,9 +1,9 @@ {% block title %}Welcome{% endblock %} | MiniTwit - -

      + +

      MiniTwit

      -