# HG changeset patch # User Steve Losh # Date 1276301543 14400 # Node ID 46b413229d3d837c57da723afe90bda63920b3d2 # Parent 07c3f5360b2200182f5a9da9bb4343ed16782317 Bundle Flask. diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/AUTHORS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/AUTHORS Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,24 @@ +Flask is written and maintained by Armin Ronacher and +various contributors: + +Development Lead +```````````````` + +- Armin Ronacher + +Patches and Suggestions +``````````````````````` + +- Chris Edgemon +- Chris Grindstaff +- Florent Xicluna +- Georg Brandl +- Justin Quick +- Kenneth Reitz +- Marian Sigler +- Matthew Frazier +- Ron DuPlain +- Sebastien Estienne +- Simon Sapin +- Stephane Wirtel +- Zhao Xiaohong diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/CHANGES --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/CHANGES Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,73 @@ +Flask Changelog +=============== + +Here you can see the full list of changes between each Flask release. + +Version 0.4 +----------- + +Release date to be announced, codename to be selected. + +- added the ability to register application wide error handlers + from modules. +- :meth:`~flask.Flask.after_request` handlers are now also invoked + if the request dies with an exception and an error handling page + kicks in. +- test client has not the ability to preserve the request context + for a little longer. This can also be used to trigger custom + requests that do not pop the request stack for testing. +- because the Python standard library caches loggers, the name of + the logger is configurable now to better support unittests. +- added `TESTING` switch that can activate unittesting helpers. + +Version 0.3.1 +------------- + +Bugfix release, released May 28th + +- fixed a error reporting bug with :meth:`flask.Config.from_envvar` +- removed some unused code from flask +- release does no longer include development leftover files (.git + folder for themes, built documentation in zip and pdf file and + some .pyc files) + +Version 0.3 +----------- + +Released on May 28th, codename Schnaps + +- added support for categories for flashed messages. +- the application now configures a :class:`logging.Handler` and will + log request handling exceptions to that logger when not in debug + mode. This makes it possible to receive mails on server errors + for example. +- added support for context binding that does not require the use of + the with statement for playing in the console. +- the request context is now available within the with statement making + it possible to further push the request context or pop it. +- added support for configurations. + +Version 0.2 +----------- + +Released on May 12th, codename Jägermeister + +- various bugfixes +- integrated JSON support +- added :func:`~flask.get_template_attribute` helper function. +- :meth:`~flask.Flask.add_url_rule` can now also register a + view function. +- refactored internal request dispatching. +- server listens on 127.0.0.1 by default now to fix issues with chrome. +- added external URL support. +- added support for :func:`~flask.send_file` +- module support and internal request handling refactoring + to better support pluggable applications. +- sessions can be set to be permanent now on a per-session basis. +- better error reporting on missing secret keys. +- added support for Google Appengine. + +Version 0.1 +----------- + +First public preview release. diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/LICENSE --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/LICENSE Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,32 @@ +Copyright (c) 2010 by Armin Ronacher and contributors. See AUTHORS +for more details. + +Some rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + +* Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +* The names of the contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/MANIFEST.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/MANIFEST.in Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,10 @@ +include Makefile CHANGES LICENSE AUTHORS +recursive-include tests * +recursive-include examples * +recursive-include docs * +recursive-exclude docs *.pyc +recursive-exclude docs *.pyo +recursive-exclude examples *.pyc +recursive-exclude examples *.pyo +prune docs/_build +prune docs/_themes/.git diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/Makefile Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,22 @@ +.PHONY: clean-pyc test upload-docs + +all: clean-pyc test + +test: + python setup.py test + +release: + python setup.py release sdist upload + +clean-pyc: + find . -name '*.pyc' -exec rm -f {} + + find . -name '*.pyo' -exec rm -f {} + + find . -name '*~' -exec rm -f {} + + +upload-docs: + $(MAKE) -C docs html dirhtml latex + $(MAKE) -C docs/_build/latex all-pdf + cd docs/_build/; mv html flask-docs; zip -r flask-docs.zip flask-docs; mv flask-docs html + scp -r docs/_build/dirhtml/* pocoo.org:/var/www/flask.pocoo.org/docs/ + scp -r docs/_build/latex/Flask.pdf pocoo.org:/var/www/flask.pocoo.org/docs/flask-docs.pdf + scp -r docs/_build/flask-docs.zip pocoo.org:/var/www/flask.pocoo.org/docs/ diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/README Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,36 @@ + + // Flask // + + web development, one drop at a time + + + ~ What is Flask? + + Flask is a microframework for Python based on Werkzeug + and Jinja2. It's intended for small scale applications + and was developped with best intentions in mind. + + ~ Is it ready? + + A preview release is out now, and I'm hoping for some + input about what you want from a microframework and + how it should look like. Consider the API to slightly + improve over time. + + ~ What do I need? + + Jinja 2.4 and Werkzeug 0.6.1. `easy_install` will + install them for you if you do `easy_install Flask==dev`. + I encourage you to use a virtualenv. Check the docs for + complete installation and usage instructions. + + ~ Where are the docs? + + Go to http://flask.pocoo.org/ for a prebuilt version of + the current documentation. Otherwise build them yourself + from the sphinx sources in the docs folder. + + ~ Where can I get help? + + Either use the #pocoo IRC channel on irc.freenode.net or + ask on the mailinglist: http://flask.pocoo.org/mailinglist/ diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/flask.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/flask.py Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,1512 @@ +# -*- coding: utf-8 -*- +""" + flask + ~~~~~ + + A microframework based on Werkzeug. It's extensively documented + and follows best practice patterns. + + :copyright: (c) 2010 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" +from __future__ import with_statement +import os +import sys +import mimetypes +from datetime import datetime, timedelta + +from itertools import chain +from threading import Lock +from jinja2 import Environment, PackageLoader, FileSystemLoader +from werkzeug import Request as RequestBase, Response as ResponseBase, \ + LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \ + ImmutableDict, cached_property, wrap_file, Headers, \ + import_string +from werkzeug.routing import Map, Rule +from werkzeug.exceptions import HTTPException, InternalServerError +from werkzeug.contrib.securecookie import SecureCookie + +# try to load the best simplejson implementation available. If JSON +# is not installed, we add a failing class. +json_available = True +try: + import simplejson as json +except ImportError: + try: + import json + except ImportError: + json_available = False + +# utilities we import from Werkzeug and Jinja2 that are unused +# in the module but are exported as public interface. +from werkzeug import abort, redirect +from jinja2 import Markup, escape + +# use pkg_resource if that works, otherwise fall back to cwd. The +# current working directory is generally not reliable with the notable +# exception of google appengine. +try: + import pkg_resources + pkg_resources.resource_stream +except (ImportError, AttributeError): + pkg_resources = None + +# a lock used for logger initialization +_logger_lock = Lock() + + +class Request(RequestBase): + """The request object used by default in flask. Remembers the + matched endpoint and view arguments. + + It is what ends up as :class:`~flask.request`. If you want to replace + the request object used you can subclass this and set + :attr:`~flask.Flask.request_class` to your subclass. + """ + + endpoint = view_args = routing_exception = None + + @property + def module(self): + """The name of the current module""" + if self.endpoint and '.' in self.endpoint: + return self.endpoint.rsplit('.', 1)[0] + + @cached_property + def json(self): + """If the mimetype is `application/json` this will contain the + parsed JSON data. + """ + if __debug__: + _assert_have_json() + if self.mimetype == 'application/json': + return json.loads(self.data) + + +class Response(ResponseBase): + """The response object that is used by default in flask. Works like the + response object from Werkzeug but is set to have a HTML mimetype by + default. Quite often you don't have to create this object yourself because + :meth:`~flask.Flask.make_response` will take care of that for you. + + If you want to replace the response object used you can subclass this and + set :attr:`~flask.Flask.response_class` to your subclass. + """ + default_mimetype = 'text/html' + + +class _RequestGlobals(object): + pass + + +class Session(SecureCookie): + """Expands the session with support for switching between permanent + and non-permanent sessions. + """ + + def _get_permanent(self): + return self.get('_permanent', False) + + def _set_permanent(self, value): + self['_permanent'] = bool(value) + + permanent = property(_get_permanent, _set_permanent) + del _get_permanent, _set_permanent + + +class _NullSession(Session): + """Class used to generate nicer error messages if sessions are not + available. Will still allow read-only access to the empty session + but fail on setting. + """ + + def _fail(self, *args, **kwargs): + raise RuntimeError('the session is unavailable because no secret ' + 'key was set. Set the secret_key on the ' + 'application to something unique and secret') + __setitem__ = __delitem__ = clear = pop = popitem = \ + update = setdefault = _fail + del _fail + + +class _RequestContext(object): + """The request context contains all request relevant information. It is + created at the beginning of the request and pushed to the + `_request_ctx_stack` and removed at the end of it. It will create the + URL adapter and request object for the WSGI environment provided. + """ + + def __init__(self, app, environ): + self.app = app + self.url_adapter = app.url_map.bind_to_environ(environ) + self.request = app.request_class(environ) + self.session = app.open_session(self.request) + if self.session is None: + self.session = _NullSession() + self.g = _RequestGlobals() + self.flashes = None + + try: + self.request.endpoint, self.request.view_args = \ + self.url_adapter.match() + except HTTPException, e: + self.request.routing_exception = e + + def push(self): + """Binds the request context.""" + _request_ctx_stack.push(self) + + def pop(self): + """Pops the request context.""" + _request_ctx_stack.pop() + + def __enter__(self): + self.push() + return self + + def __exit__(self, exc_type, exc_value, tb): + # do not pop the request stack if we are in debug mode and an + # exception happened. This will allow the debugger to still + # access the request object in the interactive shell. Furthermore + # the context can be force kept alive for the test client. + if not self.request.environ.get('flask._preserve_context') and \ + (tb is None or not self.app.debug): + self.pop() + + +def url_for(endpoint, **values): + """Generates a URL to the given endpoint with the method provided. + The endpoint is relative to the active module if modules are in use. + + Here some examples: + + ==================== ======================= ============================= + Active Module Target Endpoint Target Function + ==================== ======================= ============================= + `None` ``'index'`` `index` of the application + `None` ``'.index'`` `index` of the application + ``'admin'`` ``'index'`` `index` of the `admin` module + any ``'.index'`` `index` of the application + any ``'admin.index'`` `index` of the `admin` module + ==================== ======================= ============================= + + Variable arguments that are unknown to the target endpoint are appended + to the generated URL as query arguments. + + For more information, head over to the :ref:`Quickstart `. + + :param endpoint: the endpoint of the URL (name of the function) + :param values: the variable arguments of the URL rule + :param _external: if set to `True`, an absolute URL is generated. + """ + ctx = _request_ctx_stack.top + if '.' not in endpoint: + mod = ctx.request.module + if mod is not None: + endpoint = mod + '.' + endpoint + elif endpoint.startswith('.'): + endpoint = endpoint[1:] + external = values.pop('_external', False) + return ctx.url_adapter.build(endpoint, values, force_external=external) + + +def get_template_attribute(template_name, attribute): + """Loads a macro (or variable) a template exports. This can be used to + invoke a macro from within Python code. If you for example have a + template named `_cider.html` with the following contents: + + .. sourcecode:: html+jinja + + {% macro hello(name) %}Hello {{ name }}!{% endmacro %} + + You can access this from Python code like this:: + + hello = get_template_attribute('_cider.html', 'hello') + return hello('World') + + .. versionadded:: 0.2 + + :param template_name: the name of the template + :param attribute: the name of the variable of macro to acccess + """ + return getattr(current_app.jinja_env.get_template(template_name).module, + attribute) + + +def flash(message, category='message'): + """Flashes a message to the next request. In order to remove the + flashed message from the session and to display it to the user, + the template has to call :func:`get_flashed_messages`. + + .. versionchanged: 0.3 + `category` parameter added. + + :param message: the message to be flashed. + :param category: the category for the message. The following values + are recommended: ``'message'`` for any kind of message, + ``'error'`` for errors, ``'info'`` for information + messages and ``'warning'`` for warnings. However any + kind of string can be used as category. + """ + session.setdefault('_flashes', []).append((category, message)) + + +def get_flashed_messages(with_categories=False): + """Pulls all flashed messages from the session and returns them. + Further calls in the same request to the function will return + the same messages. By default just the messages are returned, + but when `with_categories` is set to `True`, the return value will + be a list of tuples in the form ``(category, message)`` instead. + + Example usage: + + .. sourcecode:: html+jinja + + {% for category, msg in get_flashed_messages(with_categories=true) %} +

{{ msg }} + {% endfor %} + + .. versionchanged:: 0.3 + `with_categories` parameter added. + + :param with_categories: set to `True` to also receive categories. + """ + flashes = _request_ctx_stack.top.flashes + if flashes is None: + _request_ctx_stack.top.flashes = flashes = session.pop('_flashes', []) + if not with_categories: + return [x[1] for x in flashes] + return flashes + + +def jsonify(*args, **kwargs): + """Creates a :class:`~flask.Response` with the JSON representation of + the given arguments with an `application/json` mimetype. The arguments + to this function are the same as to the :class:`dict` constructor. + + Example usage:: + + @app.route('/_get_current_user') + def get_current_user(): + return jsonify(username=g.user.username, + email=g.user.email, + id=g.user.id) + + This will send a JSON response like this to the browser:: + + { + "username": "admin", + "email": "admin@localhost", + "id": 42 + } + + This requires Python 2.6 or an installed version of simplejson. For + security reasons only objects are supported toplevel. For more + information about this, have a look at :ref:`json-security`. + + .. versionadded:: 0.2 + """ + if __debug__: + _assert_have_json() + return current_app.response_class(json.dumps(dict(*args, **kwargs), + indent=None if request.is_xhr else 2), mimetype='application/json') + + +def send_file(filename_or_fp, mimetype=None, as_attachment=False, + attachment_filename=None): + """Sends the contents of a file to the client. This will use the + most efficient method available and configured. By default it will + try to use the WSGI server's file_wrapper support. Alternatively + you can set the application's :attr:`~Flask.use_x_sendfile` attribute + to ``True`` to directly emit an `X-Sendfile` header. This however + requires support of the underlying webserver for `X-Sendfile`. + + By default it will try to guess the mimetype for you, but you can + also explicitly provide one. For extra security you probably want + to sent certain files as attachment (HTML for instance). + + Please never pass filenames to this function from user sources without + checking them first. Something like this is usually sufficient to + avoid security problems:: + + if '..' in filename or filename.startswith('/'): + abort(404) + + .. versionadded:: 0.2 + + :param filename_or_fp: the filename of the file to send. This is + relative to the :attr:`~Flask.root_path` if a + relative path is specified. + Alternatively a file object might be provided + in which case `X-Sendfile` might not work and + fall back to the traditional method. + :param mimetype: the mimetype of the file if provided, otherwise + auto detection happens. + :param as_attachment: set to `True` if you want to send this file with + a ``Content-Disposition: attachment`` header. + :param attachment_filename: the filename for the attachment if it + differs from the file's filename. + """ + if isinstance(filename_or_fp, basestring): + filename = filename_or_fp + file = None + else: + file = filename_or_fp + filename = getattr(file, 'name', None) + if filename is not None: + filename = os.path.join(current_app.root_path, filename) + if mimetype is None and (filename or attachment_filename): + mimetype = mimetypes.guess_type(filename or attachment_filename)[0] + if mimetype is None: + mimetype = 'application/octet-stream' + + headers = Headers() + if as_attachment: + if attachment_filename is None: + if filename is None: + raise TypeError('filename unavailable, required for ' + 'sending as attachment') + attachment_filename = os.path.basename(filename) + headers.add('Content-Disposition', 'attachment', + filename=attachment_filename) + + if current_app.use_x_sendfile and filename: + if file is not None: + file.close() + headers['X-Sendfile'] = filename + data = None + else: + if file is None: + file = open(filename, 'rb') + data = wrap_file(request.environ, file) + + return Response(data, mimetype=mimetype, headers=headers, + direct_passthrough=True) + + +def render_template(template_name, **context): + """Renders a template from the template folder with the given + context. + + :param template_name: the name of the template to be rendered + :param context: the variables that should be available in the + context of the template. + """ + current_app.update_template_context(context) + return current_app.jinja_env.get_template(template_name).render(context) + + +def render_template_string(source, **context): + """Renders a template from the given template source string + with the given context. + + :param template_name: the sourcecode of the template to be + rendered + :param context: the variables that should be available in the + context of the template. + """ + current_app.update_template_context(context) + return current_app.jinja_env.from_string(source).render(context) + + +def _default_template_ctx_processor(): + """Default template context processor. Injects `request`, + `session` and `g`. + """ + reqctx = _request_ctx_stack.top + return dict( + request=reqctx.request, + session=reqctx.session, + g=reqctx.g + ) + + +def _assert_have_json(): + """Helper function that fails if JSON is unavailable.""" + if not json_available: + raise RuntimeError('simplejson not installed') + + +def _get_package_path(name): + """Returns the path to a package or cwd if that cannot be found.""" + try: + return os.path.abspath(os.path.dirname(sys.modules[name].__file__)) + except (KeyError, AttributeError): + return os.getcwd() + + +# figure out if simplejson escapes slashes. This behaviour was changed +# from one version to another without reason. +if not json_available or '\\/' not in json.dumps('/'): + + def _tojson_filter(*args, **kwargs): + if __debug__: + _assert_have_json() + return json.dumps(*args, **kwargs).replace('/', '\\/') +else: + _tojson_filter = json.dumps + + +class _PackageBoundObject(object): + + def __init__(self, import_name): + #: The name of the package or module. Do not change this once + #: it was set by the constructor. + self.import_name = import_name + + #: Where is the app root located? + self.root_path = _get_package_path(self.import_name) + + def open_resource(self, resource): + """Opens a resource from the application's resource folder. To see + how this works, consider the following folder structure:: + + /myapplication.py + /schemal.sql + /static + /style.css + /templates + /layout.html + /index.html + + If you want to open the `schema.sql` file you would do the + following:: + + with app.open_resource('schema.sql') as f: + contents = f.read() + do_something_with(contents) + + :param resource: the name of the resource. To access resources within + subfolders use forward slashes as separator. + """ + if pkg_resources is None: + return open(os.path.join(self.root_path, resource), 'rb') + return pkg_resources.resource_stream(self.import_name, resource) + + +class _ModuleSetupState(object): + + def __init__(self, app, url_prefix=None): + self.app = app + self.url_prefix = url_prefix + + +class Module(_PackageBoundObject): + """Container object that enables pluggable applications. A module can + be used to organize larger applications. They represent blueprints that, + in combination with a :class:`Flask` object are used to create a large + application. + + A module is like an application bound to an `import_name`. Multiple + modules can share the same import names, but in that case a `name` has + to be provided to keep them apart. If different import names are used, + the rightmost part of the import name is used as name. + + Here an example structure for a larger appliation:: + + /myapplication + /__init__.py + /views + /__init__.py + /admin.py + /frontend.py + + The `myapplication/__init__.py` can look like this:: + + from flask import Flask + from myapplication.views.admin import admin + from myapplication.views.frontend import frontend + + app = Flask(__name__) + app.register_module(admin, url_prefix='/admin') + app.register_module(frontend) + + And here an example view module (`myapplication/views/admin.py`):: + + from flask import Module + + admin = Module(__name__) + + @admin.route('/') + def index(): + pass + + @admin.route('/login') + def login(): + pass + + For a gentle introduction into modules, checkout the + :ref:`working-with-modules` section. + """ + + def __init__(self, import_name, name=None, url_prefix=None): + if name is None: + assert '.' in import_name, 'name required if package name ' \ + 'does not point to a submodule' + name = import_name.rsplit('.', 1)[1] + _PackageBoundObject.__init__(self, import_name) + self.name = name + self.url_prefix = url_prefix + self._register_events = [] + + def route(self, rule, **options): + """Like :meth:`Flask.route` but for a module. The endpoint for the + :func:`url_for` function is prefixed with the name of the module. + """ + def decorator(f): + self.add_url_rule(rule, f.__name__, f, **options) + return f + return decorator + + def add_url_rule(self, rule, endpoint, view_func=None, **options): + """Like :meth:`Flask.add_url_rule` but for a module. The endpoint for + the :func:`url_for` function is prefixed with the name of the module. + """ + def register_rule(state): + the_rule = rule + if state.url_prefix: + the_rule = state.url_prefix + rule + state.app.add_url_rule(the_rule, '%s.%s' % (self.name, endpoint), + view_func, **options) + self._record(register_rule) + + def before_request(self, f): + """Like :meth:`Flask.before_request` but for a module. This function + is only executed before each request that is handled by a function of + that module. + """ + self._record(lambda s: s.app.before_request_funcs + .setdefault(self.name, []).append(f)) + return f + + def before_app_request(self, f): + """Like :meth:`Flask.before_request`. Such a function is executed + before each request, even if outside of a module. + """ + self._record(lambda s: s.app.before_request_funcs + .setdefault(None, []).append(f)) + return f + + def after_request(self, f): + """Like :meth:`Flask.after_request` but for a module. This function + is only executed after each request that is handled by a function of + that module. + """ + self._record(lambda s: s.app.after_request_funcs + .setdefault(self.name, []).append(f)) + return f + + def after_app_request(self, f): + """Like :meth:`Flask.after_request` but for a module. Such a function + is executed after each request, even if outside of the module. + """ + self._record(lambda s: s.app.after_request_funcs + .setdefault(None, []).append(f)) + return f + + def context_processor(self, f): + """Like :meth:`Flask.context_processor` but for a module. This + function is only executed for requests handled by a module. + """ + self._record(lambda s: s.app.template_context_processors + .setdefault(self.name, []).append(f)) + return f + + def app_context_processor(self, f): + """Like :meth:`Flask.context_processor` but for a module. Such a + function is executed each request, even if outside of the module. + """ + self._record(lambda s: s.app.template_context_processors + .setdefault(None, []).append(f)) + return f + + def app_errorhandler(self, code): + """Like :meth:`Flask.errorhandler` but for a module. This + handler is used for all requests, even if outside of the module. + + .. versionadded:: 0.4 + """ + def decorator(f): + self._record(lambda s: s.app.errorhandler(code)(f)) + return f + return decorator + + def _record(self, func): + self._register_events.append(func) + + +class ConfigAttribute(object): + """Makes an attribute forward to the config""" + + def __init__(self, name): + self.__name__ = name + + def __get__(self, obj, type=None): + if obj is None: + return self + return obj.config[self.__name__] + + def __set__(self, obj, value): + obj.config[self.__name__] = value + + +class Config(dict): + """Works exactly like a dict but provides ways to fill it from files + or special dictionaries. There are two common patterns to populate the + config. + + Either you can fill the config from a config file:: + + app.config.from_pyfile('yourconfig.cfg') + + Or alternatively you can define the configuration options in the + module that calls :meth:`from_object` or provide an import path to + a module that should be loaded. It is also possible to tell it to + use the same module and with that provide the configuration values + just before the call:: + + DEBUG = True + SECRET_KEY = 'development key' + app.config.from_object(__name__) + + In both cases (loading from any Python file or loading from modules), + only uppercase keys are added to the config. This makes it possible to use + lowercase values in the config file for temporary values that are not added + to the config or to define the config keys in the same file that implements + the application. + + Probably the most interesting way to load configurations is from an + environment variable pointing to a file:: + + app.config.from_envvar('YOURAPPLICATION_SETTINGS') + + In this case before launching the application you have to set this + environment variable to the file you want to use. On Linux and OS X + use the export statement:: + + export YOURAPPLICATION_SETTINGS='/path/to/config/file' + + On windows use `set` instead. + + :param root_path: path to which files are read relative from. When the + config object is created by the application, this is + the application's :attr:`~flask.Flask.root_path`. + :param defaults: an optional dictionary of default values + """ + + def __init__(self, root_path, defaults=None): + dict.__init__(self, defaults or {}) + self.root_path = root_path + + def from_envvar(self, variable_name, silent=False): + """Loads a configuration from an environment variable pointing to + a configuration file. This basically is just a shortcut with nicer + error messages for this line of code:: + + app.config.from_pyfile(os.environ['YOURAPPLICATION_SETTINGS']) + + :param variable_name: name of the environment variable + :param silent: set to `True` if you want silent failing for missing + files. + :return: bool. `True` if able to load config, `False` otherwise. + """ + rv = os.environ.get(variable_name) + if not rv: + if silent: + return False + raise RuntimeError('The environment variable %r is not set ' + 'and as such configuration could not be ' + 'loaded. Set this variable and make it ' + 'point to a configuration file' % + variable_name) + self.from_pyfile(rv) + return True + + def from_pyfile(self, filename): + """Updates the values in the config from a Python file. This function + behaves as if the file was imported as module with the + :meth:`from_object` function. + + :param filename: the filename of the config. This can either be an + absolute filename or a filename relative to the + root path. + """ + filename = os.path.join(self.root_path, filename) + d = type(sys)('config') + d.__file__ = filename + execfile(filename, d.__dict__) + self.from_object(d) + + def from_object(self, obj): + """Updates the values from the given object. An object can be of one + of the following two types: + + - a string: in this case the object with that name will be imported + - an actual object reference: that object is used directly + + Objects are usually either modules or classes. + + Just the uppercase variables in that object are stored in the config + after lowercasing. Example usage:: + + app.config.from_object('yourapplication.default_config') + from yourapplication import default_config + app.config.from_object(default_config) + + You should not use this function to load the actual configuration but + rather configuration defaults. The actual config should be loaded + with :meth:`from_pyfile` and ideally from a location not within the + package because the package might be installed system wide. + + :param obj: an import name or object + """ + if isinstance(obj, basestring): + obj = import_string(obj) + for key in dir(obj): + if key.isupper(): + self[key] = getattr(obj, key) + + def __repr__(self): + return '<%s %s>' % (self.__class__.__name__, dict.__repr__(self)) + + +class Flask(_PackageBoundObject): + """The flask object implements a WSGI application and acts as the central + object. It is passed the name of the module or package of the + application. Once it is created it will act as a central registry for + the view functions, the URL rules, template configuration and much more. + + The name of the package is used to resolve resources from inside the + package or the folder the module is contained in depending on if the + package parameter resolves to an actual python package (a folder with + an `__init__.py` file inside) or a standard module (just a `.py` file). + + For more information about resource loading, see :func:`open_resource`. + + Usually you create a :class:`Flask` instance in your main module or + in the `__init__.py` file of your package like this:: + + from flask import Flask + app = Flask(__name__) + """ + + #: The class that is used for request objects. See :class:`~flask.Request` + #: for more information. + request_class = Request + + #: The class that is used for response objects. See + #: :class:`~flask.Response` for more information. + response_class = Response + + #: Path for the static files. If you don't want to use static files + #: you can set this value to `None` in which case no URL rule is added + #: and the development server will no longer serve any static files. + static_path = '/static' + + #: The debug flag. Set this to `True` to enable debugging of the + #: application. In debug mode the debugger will kick in when an unhandled + #: exception ocurrs and the integrated server will automatically reload + #: the application if changes in the code are detected. + #: + #: This attribute can also be configured from the config with the `DEBUG` + #: configuration key. Defaults to `False`. + debug = ConfigAttribute('DEBUG') + + #: The testing flask. Set this to `True` to enable the test mode of + #: Flask extensions (and in the future probably also Flask itself). + #: For example this might activate unittest helpers that have an + #: additional runtime cost which should not be enabled by default. + #: + #: This attribute can also be configured from the config with the + #: `TESTING` configuration key. Defaults to `False`. + testing = ConfigAttribute('TESTING') + + #: If a secret key is set, cryptographic components can use this to + #: sign cookies and other things. Set this to a complex random value + #: when you want to use the secure cookie for instance. + #: + #: This attribute can also be configured from the config with the + #: `SECRET_KEY` configuration key. Defaults to `None`. + secret_key = ConfigAttribute('SECRET_KEY') + + #: The secure cookie uses this for the name of the session cookie. + #: + #: This attribute can also be configured from the config with the + #: `SESSION_COOKIE_NAME` configuration key. Defaults to ``'session'`` + session_cookie_name = ConfigAttribute('SESSION_COOKIE_NAME') + + #: A :class:`~datetime.timedelta` which is used to set the expiration + #: date of a permanent session. The default is 31 days which makes a + #: permanent session survive for roughly one month. + #: + #: This attribute can also be configured from the config with the + #: `PERMANENT_SESSION_LIFETIME` configuration key. Defaults to + #: ``timedelta(days=31)`` + permanent_session_lifetime = ConfigAttribute('PERMANENT_SESSION_LIFETIME') + + #: Enable this if you want to use the X-Sendfile feature. Keep in + #: mind that the server has to support this. This only affects files + #: sent with the :func:`send_file` method. + #: + #: .. versionadded:: 0.2 + #: + #: This attribute can also be configured from the config with the + #: `USE_X_SENDFILE` configuration key. Defaults to `False`. + use_x_sendfile = ConfigAttribute('USE_X_SENDFILE') + + #: The name of the logger to use. By default the logger name is the + #: package name passed to the constructor. + #: + #: .. versionadded:: 0.4 + logger_name = ConfigAttribute('LOGGER_NAME') + + #: The logging format used for the debug logger. This is only used when + #: the application is in debug mode, otherwise the attached logging + #: handler does the formatting. + #: + #: .. versionadded:: 0.3 + debug_log_format = ( + '-' * 80 + '\n' + + '%(levelname)s in %(module)s, %(pathname)s:%(lineno)d]:\n' + + '%(message)s\n' + + '-' * 80 + ) + + #: Options that are passed directly to the Jinja2 environment. + jinja_options = ImmutableDict( + autoescape=True, + extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] + ) + + #: Default configuration parameters. + default_config = ImmutableDict({ + 'DEBUG': False, + 'TESTING': False, + 'SECRET_KEY': None, + 'SESSION_COOKIE_NAME': 'session', + 'PERMANENT_SESSION_LIFETIME': timedelta(days=31), + 'USE_X_SENDFILE': False, + 'LOGGER_NAME': None + }) + + def __init__(self, import_name): + _PackageBoundObject.__init__(self, import_name) + + #: The configuration dictionary as :class:`Config`. This behaves + #: exactly like a regular dictionary but supports additional methods + #: to load a config from files. + self.config = Config(self.root_path, self.default_config) + + #: Prepare the deferred setup of the logger. + self._logger = None + self.logger_name = self.import_name + + #: A dictionary of all view functions registered. The keys will + #: be function names which are also used to generate URLs and + #: the values are the function objects themselves. + #: to register a view function, use the :meth:`route` decorator. + self.view_functions = {} + + #: A dictionary of all registered error handlers. The key is + #: be the error code as integer, the value the function that + #: should handle that error. + #: To register a error handler, use the :meth:`errorhandler` + #: decorator. + self.error_handlers = {} + + #: A dictionary with lists of functions that should be called at the + #: beginning of the request. The key of the dictionary is the name of + #: the module this function is active for, `None` for all requests. + #: This can for example be used to open database connections or + #: getting hold of the currently logged in user. To register a + #: function here, use the :meth:`before_request` decorator. + self.before_request_funcs = {} + + #: A dictionary with lists of functions that should be called after + #: each request. The key of the dictionary is the name of the module + #: this function is active for, `None` for all requests. This can for + #: example be used to open database connections or getting hold of the + #: currently logged in user. To register a function here, use the + #: :meth:`before_request` decorator. + self.after_request_funcs = {} + + #: A dictionary with list of functions that are called without argument + #: to populate the template context. They key of the dictionary is the + #: name of the module this function is active for, `None` for all + #: requests. Each returns a dictionary that the template context is + #: updated with. To register a function here, use the + #: :meth:`context_processor` decorator. + self.template_context_processors = { + None: [_default_template_ctx_processor] + } + + #: The :class:`~werkzeug.routing.Map` for this instance. You can use + #: this to change the routing converters after the class was created + #: but before any routes are connected. Example:: + #: + #: from werkzeug import BaseConverter + #: + #: class ListConverter(BaseConverter): + #: def to_python(self, value): + #: return value.split(',') + #: def to_url(self, values): + #: return ','.join(BaseConverter.to_url(value) + #: for value in values) + #: + #: app = Flask(__name__) + #: app.url_map.converters['list'] = ListConverter + self.url_map = Map() + + if self.static_path is not None: + self.add_url_rule(self.static_path + '/', + build_only=True, endpoint='static') + if pkg_resources is not None: + target = (self.import_name, 'static') + else: + target = os.path.join(self.root_path, 'static') + self.wsgi_app = SharedDataMiddleware(self.wsgi_app, { + self.static_path: target + }) + + #: The Jinja2 environment. It is created from the + #: :attr:`jinja_options` and the loader that is returned + #: by the :meth:`create_jinja_loader` function. + self.jinja_env = Environment(loader=self.create_jinja_loader(), + **self.jinja_options) + self.jinja_env.globals.update( + url_for=url_for, + get_flashed_messages=get_flashed_messages + ) + self.jinja_env.filters['tojson'] = _tojson_filter + + @property + def logger(self): + """A :class:`logging.Logger` object for this application. The + default configuration is to log to stderr if the application is + in debug mode. This logger can be used to (surprise) log messages. + Here some examples:: + + app.logger.debug('A value for debugging') + app.logger.warning('A warning ocurred (%d apples)', 42) + app.logger.error('An error occoured') + + .. versionadded:: 0.3 + """ + if self._logger and self._logger.name == self.logger_name: + return self._logger + with _logger_lock: + if self._logger and self._logger.name == self.logger_name: + return self._logger + from logging import getLogger, StreamHandler, Formatter, DEBUG + class DebugHandler(StreamHandler): + def emit(x, record): + if self.debug: + StreamHandler.emit(x, record) + handler = DebugHandler() + handler.setLevel(DEBUG) + handler.setFormatter(Formatter(self.debug_log_format)) + logger = getLogger(self.logger_name) + logger.addHandler(handler) + self._logger = logger + return logger + + def create_jinja_loader(self): + """Creates the Jinja loader. By default just a package loader for + the configured package is returned that looks up templates in the + `templates` folder. To add other loaders it's possible to + override this method. + """ + if pkg_resources is None: + return FileSystemLoader(os.path.join(self.root_path, 'templates')) + return PackageLoader(self.import_name) + + def update_template_context(self, context): + """Update the template context with some commonly used variables. + This injects request, session and g into the template context. + + :param context: the context as a dictionary that is updated in place + to add extra variables. + """ + funcs = self.template_context_processors[None] + mod = _request_ctx_stack.top.request.module + if mod is not None and mod in self.template_context_processors: + funcs = chain(funcs, self.template_context_processors[mod]) + for func in funcs: + context.update(func()) + + def run(self, host='127.0.0.1', port=5000, **options): + """Runs the application on a local development server. If the + :attr:`debug` flag is set the server will automatically reload + for code changes and show a debugger in case an exception happened. + + :param host: the hostname to listen on. set this to ``'0.0.0.0'`` + to have the server available externally as well. + :param port: the port of the webserver + :param options: the options to be forwarded to the underlying + Werkzeug server. See :func:`werkzeug.run_simple` + for more information. + """ + from werkzeug import run_simple + if 'debug' in options: + self.debug = options.pop('debug') + options.setdefault('use_reloader', self.debug) + options.setdefault('use_debugger', self.debug) + return run_simple(host, port, self, **options) + + def test_client(self): + """Creates a test client for this application. For information + about unit testing head over to :ref:`testing`. + + The test client can be used in a `with` block to defer the closing down + of the context until the end of the `with` block. This is useful if + you want to access the context locals for testing:: + + with app.test_client() as c: + rv = c.get('/?vodka=42') + assert request.args['vodka'] == '42' + + .. versionchanged:: 0.4 + added support for `with` block usage for the client. + """ + from werkzeug import Client + class FlaskClient(Client): + preserve_context = context_preserved = False + def open(self, *args, **kwargs): + if self.context_preserved: + _request_ctx_stack.pop() + self.context_preserved = False + kwargs.setdefault('environ_overrides', {}) \ + ['flask._preserve_context'] = self.preserve_context + old = _request_ctx_stack.top + try: + return Client.open(self, *args, **kwargs) + finally: + self.context_preserved = _request_ctx_stack.top is not old + def __enter__(self): + self.preserve_context = True + return self + def __exit__(self, exc_type, exc_value, tb): + self.preserve_context = False + if self.context_preserved: + _request_ctx_stack.pop() + return FlaskClient(self, self.response_class, use_cookies=True) + + def open_session(self, request): + """Creates or opens a new session. Default implementation stores all + session data in a signed cookie. This requires that the + :attr:`secret_key` is set. + + :param request: an instance of :attr:`request_class`. + """ + key = self.secret_key + if key is not None: + return Session.load_cookie(request, self.session_cookie_name, + secret_key=key) + + def save_session(self, session, response): + """Saves the session if it needs updates. For the default + implementation, check :meth:`open_session`. + + :param session: the session to be saved (a + :class:`~werkzeug.contrib.securecookie.SecureCookie` + object) + :param response: an instance of :attr:`response_class` + """ + expires = None + if session.permanent: + expires = datetime.utcnow() + self.permanent_session_lifetime + session.save_cookie(response, self.session_cookie_name, + expires=expires, httponly=True) + + def register_module(self, module, **options): + """Registers a module with this application. The keyword argument + of this function are the same as the ones for the constructor of the + :class:`Module` class and will override the values of the module if + provided. + """ + options.setdefault('url_prefix', module.url_prefix) + state = _ModuleSetupState(self, **options) + for func in module._register_events: + func(state) + + def add_url_rule(self, rule, endpoint=None, view_func=None, **options): + """Connects a URL rule. Works exactly like the :meth:`route` + decorator. If a view_func is provided it will be registered with the + endpoint. + + Basically this example:: + + @app.route('/') + def index(): + pass + + Is equivalent to the following:: + + def index(): + pass + app.add_url_rule('/', 'index', index) + + If the view_func is not provided you will need to connect the endpoint + to a view function like so:: + + app.view_functions['index'] = index + + .. versionchanged:: 0.2 + `view_func` parameter added. + + :param rule: the URL rule as string + :param endpoint: the endpoint for the registered URL rule. Flask + itself assumes the name of the view function as + endpoint + :param view_func: the function to call when serving a request to the + provided endpoint + :param options: the options to be forwarded to the underlying + :class:`~werkzeug.routing.Rule` object + """ + if endpoint is None: + assert view_func is not None, 'expected view func if endpoint ' \ + 'is not provided.' + endpoint = view_func.__name__ + options['endpoint'] = endpoint + options.setdefault('methods', ('GET',)) + self.url_map.add(Rule(rule, **options)) + if view_func is not None: + self.view_functions[endpoint] = view_func + + def route(self, rule, **options): + """A decorator that is used to register a view function for a + given URL rule. Example:: + + @app.route('/') + def index(): + return 'Hello World' + + Variables parts in the route can be specified with angular + brackets (``/user/``). By default a variable part + in the URL accepts any string without a slash however a different + converter can be specified as well by using ````. + + Variable parts are passed to the view function as keyword + arguments. + + The following converters are possible: + + =========== =========================================== + `int` accepts integers + `float` like `int` but for floating point values + `path` like the default but also accepts slashes + =========== =========================================== + + Here some examples:: + + @app.route('/') + def index(): + pass + + @app.route('/') + def show_user(username): + pass + + @app.route('/post/') + def show_post(post_id): + pass + + An important detail to keep in mind is how Flask deals with trailing + slashes. The idea is to keep each URL unique so the following rules + apply: + + 1. If a rule ends with a slash and is requested without a slash + by the user, the user is automatically redirected to the same + page with a trailing slash attached. + 2. If a rule does not end with a trailing slash and the user request + the page with a trailing slash, a 404 not found is raised. + + This is consistent with how web servers deal with static files. This + also makes it possible to use relative link targets safely. + + The :meth:`route` decorator accepts a couple of other arguments + as well: + + :param rule: the URL rule as string + :param methods: a list of methods this rule should be limited + to (``GET``, ``POST`` etc.). By default a rule + just listens for ``GET`` (and implicitly ``HEAD``). + :param subdomain: specifies the rule for the subdoain in case + subdomain matching is in use. + :param strict_slashes: can be used to disable the strict slashes + setting for this rule. See above. + :param options: other options to be forwarded to the underlying + :class:`~werkzeug.routing.Rule` object. + """ + def decorator(f): + self.add_url_rule(rule, None, f, **options) + return f + return decorator + + def errorhandler(self, code): + """A decorator that is used to register a function give a given + error code. Example:: + + @app.errorhandler(404) + def page_not_found(error): + return 'This page does not exist', 404 + + You can also register a function as error handler without using + the :meth:`errorhandler` decorator. The following example is + equivalent to the one above:: + + def page_not_found(error): + return 'This page does not exist', 404 + app.error_handlers[404] = page_not_found + + :param code: the code as integer for the handler + """ + def decorator(f): + self.error_handlers[code] = f + return f + return decorator + + def template_filter(self, name=None): + """A decorator that is used to register custom template filter. + You can specify a name for the filter, otherwise the function + name will be used. Example:: + + @app.template_filter() + def reverse(s): + return s[::-1] + + :param name: the optional name of the filter, otherwise the + function name will be used. + """ + def decorator(f): + self.jinja_env.filters[name or f.__name__] = f + return f + return decorator + + def before_request(self, f): + """Registers a function to run before each request.""" + self.before_request_funcs.setdefault(None, []).append(f) + return f + + def after_request(self, f): + """Register a function to be run after each request.""" + self.after_request_funcs.setdefault(None, []).append(f) + return f + + def context_processor(self, f): + """Registers a template context processor function.""" + self.template_context_processors[None].append(f) + return f + + def handle_http_exception(self, e): + """Handles an HTTP exception. By default this will invoke the + registered error handlers and fall back to returning the + exception as response. + + .. versionadded: 0.3 + """ + handler = self.error_handlers.get(e.code) + if handler is None: + return e + return handler(e) + + def handle_exception(self, e): + """Default exception handling that kicks in when an exception + occours that is not catched. In debug mode the exception will + be re-raised immediately, otherwise it is logged and the handler + for a 500 internal server error is used. If no such handler + exists, a default 500 internal server error message is displayed. + + .. versionadded: 0.3 + """ + handler = self.error_handlers.get(500) + if self.debug: + raise + self.logger.exception('Exception on %s [%s]' % ( + request.path, + request.method + )) + if handler is None: + return InternalServerError() + return handler(e) + + def dispatch_request(self): + """Does the request dispatching. Matches the URL and returns the + return value of the view or error handler. This does not have to + be a response object. In order to convert the return value to a + proper response object, call :func:`make_response`. + """ + req = _request_ctx_stack.top.request + try: + if req.routing_exception is not None: + raise req.routing_exception + return self.view_functions[req.endpoint](**req.view_args) + except HTTPException, e: + return self.handle_http_exception(e) + + def make_response(self, rv): + """Converts the return value from a view function to a real + response object that is an instance of :attr:`response_class`. + + The following types are allowed for `rv`: + + .. tabularcolumns:: |p{3.5cm}|p{9.5cm}| + + ======================= =========================================== + :attr:`response_class` the object is returned unchanged + :class:`str` a response object is created with the + string as body + :class:`unicode` a response object is created with the + string encoded to utf-8 as body + :class:`tuple` the response object is created with the + contents of the tuple as arguments + a WSGI function the function is called as WSGI application + and buffered as response object + ======================= =========================================== + + :param rv: the return value from the view function + """ + if rv is None: + raise ValueError('View function did not return a response') + if isinstance(rv, self.response_class): + return rv + if isinstance(rv, basestring): + return self.response_class(rv) + if isinstance(rv, tuple): + return self.response_class(*rv) + return self.response_class.force_type(rv, request.environ) + + def preprocess_request(self): + """Called before the actual request dispatching and will + call every as :meth:`before_request` decorated function. + If any of these function returns a value it's handled as + if it was the return value from the view and further + request handling is stopped. + """ + funcs = self.before_request_funcs.get(None, ()) + mod = request.module + if mod and mod in self.before_request_funcs: + funcs = chain(funcs, self.before_request_funcs[mod]) + for func in funcs: + rv = func() + if rv is not None: + return rv + + def process_response(self, response): + """Can be overridden in order to modify the response object + before it's sent to the WSGI server. By default this will + call all the :meth:`after_request` decorated functions. + + :param response: a :attr:`response_class` object. + :return: a new response object or the same, has to be an + instance of :attr:`response_class`. + """ + ctx = _request_ctx_stack.top + mod = ctx.request.module + if not isinstance(ctx.session, _NullSession): + self.save_session(ctx.session, response) + funcs = () + if mod and mod in self.after_request_funcs: + funcs = chain(funcs, self.after_request_funcs[mod]) + if None in self.after_request_funcs: + funcs = chain(funcs, self.after_request_funcs[None]) + for handler in funcs: + response = handler(response) + return response + + def wsgi_app(self, environ, start_response): + """The actual WSGI application. This is not implemented in + `__call__` so that middlewares can be applied without losing a + reference to the class. So instead of doing this:: + + app = MyMiddleware(app) + + It's a better idea to do this instead:: + + app.wsgi_app = MyMiddleware(app.wsgi_app) + + Then you still have the original application object around and + can continue to call methods on it. + + .. versionchanged:: 0.4 + The :meth:`after_request` functions are now called even if an + error handler took over request processing. This ensures that + even if an exception happens database have the chance to + properly close the connection. + + :param environ: a WSGI environment + :param start_response: a callable accepting a status code, + a list of headers and an optional + exception context to start the response + """ + with self.request_context(environ): + try: + rv = self.preprocess_request() + if rv is None: + rv = self.dispatch_request() + response = self.make_response(rv) + except Exception, e: + response = self.make_response(self.handle_exception(e)) + try: + response = self.process_response(response) + except Exception, e: + response = self.make_response(self.handle_exception(e)) + return response(environ, start_response) + + def request_context(self, environ): + """Creates a request context from the given environment and binds + it to the current context. This must be used in combination with + the `with` statement because the request is only bound to the + current context for the duration of the `with` block. + + Example usage:: + + with app.request_context(environ): + do_something_with(request) + + The object returned can also be used without the `with` statement + which is useful for working in the shell. The example above is + doing exactly the same as this code:: + + ctx = app.request_context(environ) + ctx.push() + try: + do_something_with(request) + finally: + ctx.pop() + + The big advantage of this approach is that you can use it without + the try/finally statement in a shell for interactive testing: + + >>> ctx = app.test_request_context() + >>> ctx.bind() + >>> request.path + u'/' + >>> ctx.unbind() + + .. versionchanged:: 0.3 + Added support for non-with statement usage and `with` statement + is now passed the ctx object. + + :param environ: a WSGI environment + """ + return _RequestContext(self, environ) + + def test_request_context(self, *args, **kwargs): + """Creates a WSGI environment from the given values (see + :func:`werkzeug.create_environ` for more information, this + function accepts the same arguments). + """ + return self.request_context(create_environ(*args, **kwargs)) + + def __call__(self, environ, start_response): + """Shortcut for :attr:`wsgi_app`.""" + return self.wsgi_app(environ, start_response) + + +# context locals +_request_ctx_stack = LocalStack() +current_app = LocalProxy(lambda: _request_ctx_stack.top.app) +request = LocalProxy(lambda: _request_ctx_stack.top.request) +session = LocalProxy(lambda: _request_ctx_stack.top.session) +g = LocalProxy(lambda: _request_ctx_stack.top.g) diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/setup.cfg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/setup.cfg Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,6 @@ +[egg_info] +tag_build = dev +tag_date = true + +[aliases] +release = egg_info -RDb '' diff -r 07c3f5360b22 -r 46b413229d3d bundled/flask/setup.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/bundled/flask/setup.py Fri Jun 11 20:12:23 2010 -0400 @@ -0,0 +1,79 @@ +""" +Flask +----- + +Flask is a microframework for Python based on Werkzeug, Jinja 2 and good +intentions. And before you ask: It's BSD licensed! + +Flask is Fun +```````````` + +:: + + from flask import Flask + app = Flask(__name__) + + @app.route("/") + def hello(): + return "Hello World!" + + if __name__ == "__main__": + app.run() + +And Easy to Setup +````````````````` + +:: + + $ easy_install Flask + $ python hello.py + * Running on http://localhost:5000/ + +Links +````` + +* `website `_ +* `documentation `_ +* `development version + `_ + +""" +from setuptools import setup + + +def run_tests(): + import os, sys + sys.path.append(os.path.join(os.path.dirname(__file__), 'tests')) + from flask_tests import suite + return suite() + + +setup( + name='Flask', + version='0.4', + url='http://github.com/mitsuhiko/flask/', + license='BSD', + author='Armin Ronacher', + author_email='armin.ronacher@active-4.com', + description='A microframework based on Werkzeug, Jinja2 ' + 'and good intentions', + long_description=__doc__, + py_modules=['flask'], + zip_safe=False, + platforms='any', + install_requires=[ + 'Werkzeug>=0.6.1', + 'Jinja2>=2.4' + ], + classifiers=[ + 'Development Status :: 4 - Beta', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', + 'Topic :: Software Development :: Libraries :: Python Modules' + ], + test_suite='__main__.run_tests' +)