# -*- 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 from jinja2 import Environment, PackageLoader, FileSystemLoader from werkzeug import Request as RequestBase, Response as ResponseBase, \ LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \ cached_property from werkzeug.routing import Map, Rule from werkzeug.exceptions import HTTPException 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 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. """ def __init__(self, environ): RequestBase.__init__(self, environ) self.endpoint = None self.view_args = None @cached_property def json(self): """If the mimetype is `application/json` this will contain the parsed JSON data. """ if not json_available: raise AttributeError('simplejson not available') 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.request_class` to your subclass. """ default_mimetype = 'text/html' class _RequestGlobals(object): pass class _NullSession(SecureCookie): """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 def __enter__(self): _request_ctx_stack.push(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. if tb is None or not self.app.debug: _request_ctx_stack.pop() def url_for(endpoint, **values): """Generates a URL to the given endpoint with the method provided. :param endpoint: the endpoint of the URL (name of the function) :param values: the variable arguments of the URL rule """ return _request_ctx_stack.top.url_adapter.build(endpoint, values) 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 `_foo.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('_foo.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): """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`. :param message: the message to be flashed. """ session['_flashes'] = (session.get('_flashes', [])) + [message] def get_flashed_messages(): """Pulls all flashed messages from the session and returns them. Further calls in the same request to the function will return the same messages. """ flashes = _request_ctx_stack.top.flashes if flashes is None: _request_ctx_stack.top.flashes = flashes = \ session.pop('_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. .. versionadded:: 0.2 """ return current_app.response_class(json.dumps(dict(*args, **kwargs), indent=None if request.is_xhr else 2), mimetype='application/json') 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 _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() def _tojson_filter(string, *args, **kwargs): """Calls dumps for the template engine, escaping Slashes properly.""" return json.dumps(string, *args, **kwargs).replace('/', '\\/') class Flask(object): """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' #: 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. secret_key = None #: The secure cookie uses this for the name of the session cookie session_cookie_name = 'session' #: options that are passed directly to the Jinja2 environment jinja_options = dict( autoescape=True, extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] ) def __init__(self, package_name): #: 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. self.debug = False #: the name of the package or module. Do not change this once #: it was set by the constructor. self.package_name = package_name #: where is the app root located? self.root_path = _get_package_path(self.package_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 list of functions that should be called at the beginning #: of the request before request dispatching kicks in. 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 list of functions that are called at the end of the #: request. Tha function is passed the current response #: object and modify it in place or replace it. #: To register a function here use the :meth:`after_request` #: decorator. self.after_request_funcs = [] #: a list of functions that are called without arguments #: to populate the template context. 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 = [_default_template_ctx_processor] self.url_map = Map() if self.static_path is not None: self.url_map.add(Rule(self.static_path + '/', build_only=True, endpoint='static')) if pkg_resources is not None: target = (self.package_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 ) if json_available: self.jinja_env.filters['tojson'] = _tojson_filter 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.package_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. """ for func in self.template_context_processors: context.update(func()) def run(self, host='localhost', 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`. """ from werkzeug import Client return Client(self, self.response_class, use_cookies=True) 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 /template /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.package_name, resource) 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 SecureCookie.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` """ session.save_cookie(response, self.session_cookie_name) def add_url_rule(self, rule, endpoint, **options): """Connects a URL rule. Works exactly like the :meth:`route` decorator but does not register the view function for the endpoint. Basically this example:: @app.route('/') def index(): pass Is equivalent to the following:: def index(): pass app.add_url_rule('index', '/') app.view_functions['index'] = index :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 options: the options to be forwarded to the underlying :class:`~werkzeug.routing.Rule` object """ options['endpoint'] = endpoint options.setdefault('methods', ('GET',)) self.url_map.add(Rule(rule, **options)) 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, f.__name__, **options) self.view_functions[f.__name__] = f 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(): 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(): 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 before_request(self, f): """Registers a function to run before each request.""" self.before_request_funcs.append(f) return f def after_request(self, f): """Register a function to be run after each request.""" self.after_request_funcs.append(f) return f def context_processor(self, f): """Registers a template context processor function.""" self.template_context_processors.append(f) return f def match_request(self): """Matches the current request against the URL map and also stores the endpoint and view arguments on the request object is successful, otherwise the exception is stored. """ rv = _request_ctx_stack.top.url_adapter.match() request.endpoint, request.view_args = rv return rv 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`. """ try: endpoint, values = self.match_request() return self.view_functions[endpoint](**values) except HTTPException, e: handler = self.error_handlers.get(e.code) if handler is None: return e return handler(e) except Exception, e: handler = self.error_handlers.get(500) if self.debug or handler is None: raise return handler(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 allowd for `rv`: ======================= =========================================== :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 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. """ for func in self.before_request_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`. """ session = _request_ctx_stack.top.session if not isinstance(session, _NullSession): self.save_session(session, response) for handler in self.after_request_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. :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): rv = self.preprocess_request() if rv is None: rv = self.dispatch_request() response = self.make_response(rv) response = self.process_response(response) 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) :params 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)