|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
"""
|
|
|
|
flask.module
|
|
|
|
~~~~~~~~~~~~
|
|
|
|
|
|
|
|
Implements a class that represents module blueprints.
|
|
|
|
|
|
|
|
:copyright: (c) 2010 by Armin Ronacher.
|
|
|
|
:license: BSD, see LICENSE for more details.
|
|
|
|
"""
|
|
|
|
|
|
|
|
from .helpers import _PackageBoundObject, _endpoint_from_view_func
|
|
|
|
|
|
|
|
|
|
|
|
def _register_module(module, static_path):
|
|
|
|
"""Internal helper function that returns a function for recording
|
|
|
|
that registers the `send_static_file` function for the module on
|
|
|
|
the application if necessary. It also registers the module on
|
|
|
|
the application.
|
|
|
|
"""
|
|
|
|
def _register(state):
|
|
|
|
state.app.modules[module.name] = module
|
|
|
|
# do not register the rule if the static folder of the
|
|
|
|
# module is the same as the one from the application.
|
|
|
|
if state.app.root_path == module.root_path:
|
|
|
|
return
|
|
|
|
path = static_path
|
|
|
|
if path is None:
|
|
|
|
path = state.app.static_path
|
|
|
|
if state.url_prefix:
|
|
|
|
path = state.url_prefix + path
|
|
|
|
state.app.add_url_rule(path + '/<path:filename>',
|
|
|
|
endpoint='%s.static' % module.name,
|
|
|
|
view_func=module.send_static_file)
|
|
|
|
return _register
|
|
|
|
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
.. versionadded:: 0.5
|
|
|
|
The `static_path` parameter was added and it's now possible for
|
|
|
|
modules to refer to their own templates and static files. See
|
|
|
|
:ref:`modules-and-resources` for more information.
|
|
|
|
|
|
|
|
:param import_name: the name of the Python package or module
|
|
|
|
implementing this :class:`Module`.
|
|
|
|
:param name: the internal short name for the module. Unless specified
|
|
|
|
the rightmost part of the import name
|
|
|
|
:param url_prefix: an optional string that is used to prefix all the
|
|
|
|
URL rules of this module. This can also be specified
|
|
|
|
when registering the module with the application.
|
|
|
|
:param static_path: can be used to specify a different path for the
|
|
|
|
static files on the web. Defaults to ``/static``.
|
|
|
|
This does not affect the folder the files are served
|
|
|
|
*from*.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self, import_name, name=None, url_prefix=None,
|
|
|
|
static_path=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 = [_register_module(self, static_path)]
|
|
|
|
|
|
|
|
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=None, 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.
|
|
|
|
|
|
|
|
.. versionchanged:: 0.6
|
|
|
|
The `endpoint` argument is now optional and will default to the
|
|
|
|
function name to consistent with the function of the same name
|
|
|
|
on the application object.
|
|
|
|
"""
|
|
|
|
def register_rule(state):
|
|
|
|
the_rule = rule
|
|
|
|
if state.url_prefix:
|
|
|
|
the_rule = state.url_prefix + rule
|
|
|
|
the_endpoint = endpoint
|
|
|
|
if the_endpoint is None:
|
|
|
|
the_endpoint = _endpoint_from_view_func(view_func)
|
|
|
|
state.app.add_url_rule(the_rule, '%s.%s' % (self.name,
|
|
|
|
the_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)
|