|
|
|
@ -1,802 +1,25 @@
|
|
|
|
|
# -*- 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 |
|
|
|
|
|
|
|
|
|
# this is a workaround for appengine. Do not remove this import |
|
|
|
|
import werkzeug |
|
|
|
|
|
|
|
|
|
from itertools import chain |
|
|
|
|
from threading import Lock |
|
|
|
|
from datetime import timedelta, datetime |
|
|
|
|
from itertools import chain |
|
|
|
|
|
|
|
|
|
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 import ImmutableDict, SharedDataMiddleware, create_environ |
|
|
|
|
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 |
|
|
|
|
from flask.helpers import _PackageBoundObject, url_for, get_flashed_messages, \ |
|
|
|
|
_tojson_filter, get_pkg_resources |
|
|
|
|
from flask.wrappers import Request, Response |
|
|
|
|
from flask.conf import ConfigAttribute, Config |
|
|
|
|
from flask.ctx import _default_template_ctx_processor, _RequestContext |
|
|
|
|
from flask.globals import _request_ctx_stack, request |
|
|
|
|
from flask.session import Session, _NullSession |
|
|
|
|
from flask.module import _ModuleSetupState |
|
|
|
|
|
|
|
|
|
# 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. |
|
|
|
|
""" |
|
|
|
|
|
|
|
|
|
#: the endpoint that matched the request. This in combination with |
|
|
|
|
#: :attr:`view_args` can be used to reconstruct the same or a |
|
|
|
|
#: modified URL. If an exception happened when matching, this will |
|
|
|
|
#: be `None`. |
|
|
|
|
endpoint = None |
|
|
|
|
|
|
|
|
|
#: a dict of view arguments that matched the request. If an exception |
|
|
|
|
#: happened when matching, this will be `None`. |
|
|
|
|
view_args = None |
|
|
|
|
|
|
|
|
|
#: if matching the URL failed, this is the exception that will be |
|
|
|
|
#: raised / was raised as part of the request handling. This is |
|
|
|
|
#: usually a :exc:`~werkzeug.exceptions.NotFound` exception or |
|
|
|
|
#: something similar. |
|
|
|
|
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, |
|
|
|
|
server_name=app.config['SERVER_NAME']) |
|
|
|
|
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 <url-building>`. |
|
|
|
|
|
|
|
|
|
: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) %} |
|
|
|
|
<p class=flash-{{ category }}>{{ 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)) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _select_autoescape(filename): |
|
|
|
|
"""Returns `True` if autoescaping should be active for the given |
|
|
|
|
template name. |
|
|
|
|
""" |
|
|
|
|
if filename is None: |
|
|
|
|
return False |
|
|
|
|
return filename.endswith(('.html', '.htm', '.xml', '.xhtml')) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 |
|
|
|
@ -929,7 +152,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
|
|
|
|
|
#: Options that are passed directly to the Jinja2 environment. |
|
|
|
|
jinja_options = ImmutableDict( |
|
|
|
|
autoescape=_select_autoescape, |
|
|
|
|
autoescape=True, |
|
|
|
|
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_'] |
|
|
|
|
) |
|
|
|
|
|
|
|
|
@ -1016,7 +239,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
if self.static_path is not None: |
|
|
|
|
self.add_url_rule(self.static_path + '/<filename>', |
|
|
|
|
build_only=True, endpoint='static') |
|
|
|
|
if pkg_resources is not None: |
|
|
|
|
if get_pkg_resources() is not None: |
|
|
|
|
target = (self.import_name, 'static') |
|
|
|
|
else: |
|
|
|
|
target = os.path.join(self.root_path, 'static') |
|
|
|
@ -1027,8 +250,13 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
#: 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 = self.create_jinja_environment() |
|
|
|
|
self.init_jinja_globals() |
|
|
|
|
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): |
|
|
|
@ -1065,38 +293,16 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
self._logger = logger |
|
|
|
|
return logger |
|
|
|
|
|
|
|
|
|
def create_jinja_environment(self): |
|
|
|
|
"""Creates the Jinja2 environment based on :attr:`jinja_options` |
|
|
|
|
and :meth:`create_jinja_loader`. |
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.5 |
|
|
|
|
""" |
|
|
|
|
return Environment(loader=self.create_jinja_loader(), |
|
|
|
|
**self.jinja_options) |
|
|
|
|
|
|
|
|
|
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: |
|
|
|
|
if get_pkg_resources() is None: |
|
|
|
|
return FileSystemLoader(os.path.join(self.root_path, 'templates')) |
|
|
|
|
return PackageLoader(self.import_name) |
|
|
|
|
|
|
|
|
|
def init_jinja_globals(self): |
|
|
|
|
"""Callde directly after the environment was created to inject |
|
|
|
|
some defaults (like `url_for`, `get_flashed_messages` and the |
|
|
|
|
`tojson` filter. |
|
|
|
|
|
|
|
|
|
.. versionadded:: 0.5 |
|
|
|
|
""" |
|
|
|
|
self.jinja_env.globals.update( |
|
|
|
|
url_for=url_for, |
|
|
|
|
get_flashed_messages=get_flashed_messages |
|
|
|
|
) |
|
|
|
|
self.jinja_env.filters['tojson'] = _tojson_filter |
|
|
|
|
|
|
|
|
|
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. |
|
|
|
@ -1318,7 +524,7 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
: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 subdomain in case |
|
|
|
|
: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. |
|
|
|
@ -1590,10 +796,3 @@ class Flask(_PackageBoundObject):
|
|
|
|
|
"""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) |