Merged in changes from justquick
This commit is contained in:
commit
81148db5b6
10 changed files with 833 additions and 827 deletions
23
flask/__init__.py
Normal file
23
flask/__init__.py
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
# -*- 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.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
from flask.app import Flask
|
||||||
|
from flask.helpers import url_for, jsonify, json_available, flash, send_file, \
|
||||||
|
get_flashed_messages, render_template, render_template, render_template_string, \
|
||||||
|
get_template_attribute
|
||||||
|
from flask.globals import current_app, g, request, session, _request_ctx_stack
|
||||||
|
from flask.module import Module
|
||||||
|
|
@ -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 threading import Lock
|
||||||
|
from datetime import timedelta, datetime
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from jinja2 import Environment, PackageLoader, FileSystemLoader
|
from jinja2 import Environment, PackageLoader, FileSystemLoader
|
||||||
from werkzeug import Request as RequestBase, Response as ResponseBase, \
|
from werkzeug import ImmutableDict, SharedDataMiddleware, create_environ
|
||||||
LocalStack, LocalProxy, create_environ, SharedDataMiddleware, \
|
|
||||||
ImmutableDict, cached_property, wrap_file, Headers, \
|
|
||||||
import_string
|
|
||||||
from werkzeug.routing import Map, Rule
|
from werkzeug.routing import Map, Rule
|
||||||
from werkzeug.exceptions import HTTPException, InternalServerError
|
from werkzeug.exceptions import HTTPException, InternalServerError
|
||||||
from werkzeug.contrib.securecookie import SecureCookie
|
|
||||||
|
|
||||||
# try to load the best simplejson implementation available. If JSON
|
from flask.helpers import _PackageBoundObject, url_for, get_flashed_messages, \
|
||||||
# is not installed, we add a failing class.
|
_tojson_filter, get_pkg_resources
|
||||||
json_available = True
|
from flask.wrappers import Request, Response
|
||||||
try:
|
from flask.conf import ConfigAttribute, Config
|
||||||
import simplejson as json
|
from flask.ctx import _default_template_ctx_processor, _RequestContext
|
||||||
except ImportError:
|
from flask.globals import _request_ctx_stack, request
|
||||||
try:
|
from flask.session import Session, _NullSession
|
||||||
import json
|
from flask.module import _ModuleSetupState
|
||||||
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
|
# a lock used for logger initialization
|
||||||
_logger_lock = Lock()
|
_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):
|
class Flask(_PackageBoundObject):
|
||||||
"""The flask object implements a WSGI application and acts as the central
|
"""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
|
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.
|
#: Options that are passed directly to the Jinja2 environment.
|
||||||
jinja_options = ImmutableDict(
|
jinja_options = ImmutableDict(
|
||||||
autoescape=_select_autoescape,
|
autoescape=True,
|
||||||
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
|
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1016,7 +239,7 @@ class Flask(_PackageBoundObject):
|
||||||
if self.static_path is not None:
|
if self.static_path is not None:
|
||||||
self.add_url_rule(self.static_path + '/<filename>',
|
self.add_url_rule(self.static_path + '/<filename>',
|
||||||
build_only=True, endpoint='static')
|
build_only=True, endpoint='static')
|
||||||
if pkg_resources is not None:
|
if get_pkg_resources() is not None:
|
||||||
target = (self.import_name, 'static')
|
target = (self.import_name, 'static')
|
||||||
else:
|
else:
|
||||||
target = os.path.join(self.root_path, 'static')
|
target = os.path.join(self.root_path, 'static')
|
||||||
|
|
@ -1027,8 +250,13 @@ class Flask(_PackageBoundObject):
|
||||||
#: The Jinja2 environment. It is created from the
|
#: The Jinja2 environment. It is created from the
|
||||||
#: :attr:`jinja_options` and the loader that is returned
|
#: :attr:`jinja_options` and the loader that is returned
|
||||||
#: by the :meth:`create_jinja_loader` function.
|
#: by the :meth:`create_jinja_loader` function.
|
||||||
self.jinja_env = self.create_jinja_environment()
|
self.jinja_env = Environment(loader=self.create_jinja_loader(),
|
||||||
self.init_jinja_globals()
|
**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
|
@property
|
||||||
def logger(self):
|
def logger(self):
|
||||||
|
|
@ -1065,38 +293,16 @@ class Flask(_PackageBoundObject):
|
||||||
self._logger = logger
|
self._logger = logger
|
||||||
return 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):
|
def create_jinja_loader(self):
|
||||||
"""Creates the Jinja loader. By default just a package loader for
|
"""Creates the Jinja loader. By default just a package loader for
|
||||||
the configured package is returned that looks up templates in the
|
the configured package is returned that looks up templates in the
|
||||||
`templates` folder. To add other loaders it's possible to
|
`templates` folder. To add other loaders it's possible to
|
||||||
override this method.
|
override this method.
|
||||||
"""
|
"""
|
||||||
if pkg_resources is None:
|
if get_pkg_resources() is None:
|
||||||
return FileSystemLoader(os.path.join(self.root_path, 'templates'))
|
return FileSystemLoader(os.path.join(self.root_path, 'templates'))
|
||||||
return PackageLoader(self.import_name)
|
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):
|
def update_template_context(self, context):
|
||||||
"""Update the template context with some commonly used variables.
|
"""Update the template context with some commonly used variables.
|
||||||
This injects request, session and g into the template context.
|
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
|
:param methods: a list of methods this rule should be limited
|
||||||
to (``GET``, ``POST`` etc.). By default a rule
|
to (``GET``, ``POST`` etc.). By default a rule
|
||||||
just listens for ``GET`` (and implicitly ``HEAD``).
|
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.
|
subdomain matching is in use.
|
||||||
:param strict_slashes: can be used to disable the strict slashes
|
:param strict_slashes: can be used to disable the strict slashes
|
||||||
setting for this rule. See above.
|
setting for this rule. See above.
|
||||||
|
|
@ -1590,10 +796,3 @@ class Flask(_PackageBoundObject):
|
||||||
"""Shortcut for :attr:`wsgi_app`."""
|
"""Shortcut for :attr:`wsgi_app`."""
|
||||||
return self.wsgi_app(environ, start_response)
|
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)
|
|
||||||
139
flask/conf.py
Normal file
139
flask/conf.py
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
from werkzeug import import_string
|
||||||
|
|
||||||
|
|
||||||
|
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))
|
||||||
62
flask/ctx.py
Normal file
62
flask/ctx.py
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
from werkzeug.exceptions import HTTPException
|
||||||
|
|
||||||
|
from flask.wrappers import _RequestGlobals
|
||||||
|
from flask.globals import _request_ctx_stack
|
||||||
|
from flask.session import _NullSession
|
||||||
|
|
||||||
|
|
||||||
|
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 _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
|
||||||
|
)
|
||||||
8
flask/globals.py
Normal file
8
flask/globals.py
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
from werkzeug import LocalStack, LocalProxy
|
||||||
|
|
||||||
|
# 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)
|
||||||
328
flask/helpers.py
Normal file
328
flask/helpers.py
Normal file
|
|
@ -0,0 +1,328 @@
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
import mimetypes
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
from werkzeug import Headers, wrap_file
|
||||||
|
|
||||||
|
from flask.globals import session, _request_ctx_stack, current_app, request
|
||||||
|
from flask.wrappers import Response
|
||||||
|
|
||||||
|
|
||||||
|
def _assert_have_json():
|
||||||
|
"""Helper function that fails if JSON is unavailable."""
|
||||||
|
if not json_available:
|
||||||
|
raise RuntimeError('simplejson not installed')
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
|
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 get_pkg_resources():
|
||||||
|
"""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):
|
||||||
|
return
|
||||||
|
return pkg_resources
|
||||||
|
|
||||||
|
|
||||||
|
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 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 _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()
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
"""
|
||||||
|
pkg_resources = get_pkg_resources()
|
||||||
|
if pkg_resources is None:
|
||||||
|
return open(os.path.join(self.root_path, resource), 'rb')
|
||||||
|
return pkg_resources.resource_stream(self.import_name, resource)
|
||||||
152
flask/module.py
Normal file
152
flask/module.py
Normal file
|
|
@ -0,0 +1,152 @@
|
||||||
|
from flask.helpers import _PackageBoundObject
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
31
flask/session.py
Normal file
31
flask/session.py
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
from werkzeug.contrib.securecookie import SecureCookie
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
64
flask/wrappers.py
Normal file
64
flask/wrappers.py
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
from werkzeug import Request as RequestBase, Response as ResponseBase, \
|
||||||
|
cached_property
|
||||||
|
|
||||||
|
from helpers import json
|
||||||
|
|
||||||
|
|
||||||
|
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__:
|
||||||
|
from flask.helpers import _assert_have_json
|
||||||
|
_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
|
||||||
|
|
||||||
|
|
@ -21,7 +21,7 @@ from contextlib import contextmanager
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from werkzeug import parse_date, parse_options_header
|
from werkzeug import parse_date, parse_options_header
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
|
from flask.helpers import json
|
||||||
|
|
||||||
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
|
example_path = os.path.join(os.path.dirname(__file__), '..', 'examples')
|
||||||
sys.path.append(os.path.join(example_path, 'flaskr'))
|
sys.path.append(os.path.join(example_path, 'flaskr'))
|
||||||
|
|
@ -409,7 +409,7 @@ class JSONTestCase(unittest.TestCase):
|
||||||
for url in '/kw', '/dict':
|
for url in '/kw', '/dict':
|
||||||
rv = c.get(url)
|
rv = c.get(url)
|
||||||
assert rv.mimetype == 'application/json'
|
assert rv.mimetype == 'application/json'
|
||||||
assert flask.json.loads(rv.data) == d
|
assert json.loads(rv.data) == d
|
||||||
|
|
||||||
def test_json_attr(self):
|
def test_json_attr(self):
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
|
|
@ -417,7 +417,7 @@ class JSONTestCase(unittest.TestCase):
|
||||||
def add():
|
def add():
|
||||||
return unicode(flask.request.json['a'] + flask.request.json['b'])
|
return unicode(flask.request.json['a'] + flask.request.json['b'])
|
||||||
c = app.test_client()
|
c = app.test_client()
|
||||||
rv = c.post('/add', data=flask.json.dumps({'a': 1, 'b': 2}),
|
rv = c.post('/add', data=json.dumps({'a': 1, 'b': 2}),
|
||||||
content_type='application/json')
|
content_type='application/json')
|
||||||
assert rv.data == '3'
|
assert rv.data == '3'
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue