Modules are now instanciated with the package name.
This makes it possible to load resources from the folder the module is located in.
This commit is contained in:
parent
2f181e1949
commit
67f4b0f315
2 changed files with 96 additions and 73 deletions
165
flask.py
165
flask.py
|
|
@ -295,6 +295,43 @@ 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
|
||||
/template
|
||||
/layout.html
|
||||
/index.html
|
||||
|
||||
If you want to open the `schema.sql` file you would do the
|
||||
following::
|
||||
|
||||
with app.open_resource('schema.sql') as f:
|
||||
contents = f.read()
|
||||
do_something_with(contents)
|
||||
|
||||
:param resource: the name of the resource. To access resources within
|
||||
subfolders use forward slashes as separator.
|
||||
"""
|
||||
if pkg_resources is None:
|
||||
return open(os.path.join(self.root_path, resource), 'rb')
|
||||
return pkg_resources.resource_stream(self.import_name, resource)
|
||||
|
||||
|
||||
class _ModuleSetupState(object):
|
||||
|
||||
def __init__(self, app, url_prefix=None):
|
||||
|
|
@ -302,44 +339,53 @@ class _ModuleSetupState(object):
|
|||
self.url_prefix = url_prefix
|
||||
|
||||
|
||||
class Module(object):
|
||||
class Module(_PackageBoundObject):
|
||||
"""Container object that enables pluggable applications"""
|
||||
|
||||
def __init__(self, name, url_prefix=None):
|
||||
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.Flask.route` but for a 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):
|
||||
self._record(self._register_rule, (rule, endpoint, view_func, options))
|
||||
"""Like :meth:`flask.Flask.add_url_rule` but for a module"""
|
||||
def register_rule(state):
|
||||
the_rule = rule
|
||||
if self.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.Flask.before_request` but for a module"""
|
||||
self._record(lambda s: s.app.before_request_funcs
|
||||
.setdefault(self.name, []).append(f), ())
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def after_request(self, f):
|
||||
"""Like :meth:`flask.Flask.after_request` but for a module"""
|
||||
self._record(lambda s: s.app.after_request_funcs
|
||||
.setdefault(self.name, []).append(f), ())
|
||||
.setdefault(self.name, []).append(f))
|
||||
return f
|
||||
|
||||
def _record(self, func, args):
|
||||
self._register_events.append((func, args))
|
||||
|
||||
def _register_rule(self, state, rule, endpoint, view_func, options):
|
||||
if self.url_prefix:
|
||||
rule = state.url_prefix + rule
|
||||
state.app.add_url_rule(rule, '%s.%s' % (self.name, endpoint),
|
||||
view_func, **options)
|
||||
def _record(self, func):
|
||||
self._register_events.append(func)
|
||||
|
||||
|
||||
class Flask(object):
|
||||
class Flask(_PackageBoundObject):
|
||||
"""The flask object implements a WSGI application and acts as the central
|
||||
object. It is passed the name of the module or package of the
|
||||
application. Once it is created it will act as a central registry for
|
||||
|
|
@ -386,7 +432,9 @@ class Flask(object):
|
|||
extensions=['jinja2.ext.autoescape', 'jinja2.ext.with_']
|
||||
)
|
||||
|
||||
def __init__(self, package_name):
|
||||
def __init__(self, import_name):
|
||||
_PackageBoundObject.__init__(self, import_name)
|
||||
|
||||
#: the debug flag. Set this to `True` to enable debugging of
|
||||
#: the application. In debug mode the debugger will kick in
|
||||
#: when an unhandled exception ocurrs and the integrated server
|
||||
|
|
@ -394,13 +442,6 @@ class Flask(object):
|
|||
#: code are detected.
|
||||
self.debug = False
|
||||
|
||||
#: the name of the package or module. Do not change this once
|
||||
#: it was set by the constructor.
|
||||
self.package_name = package_name
|
||||
|
||||
#: where is the app root located?
|
||||
self.root_path = _get_package_path(self.package_name)
|
||||
|
||||
#: a dictionary of all view functions registered. The keys will
|
||||
#: be function names which are also used to generate URLs and
|
||||
#: the values are the function objects themselves.
|
||||
|
|
@ -414,27 +455,31 @@ class Flask(object):
|
|||
#: decorator.
|
||||
self.error_handlers = {}
|
||||
|
||||
#: a list of functions that should be called at the beginning
|
||||
#: of the request before request dispatching kicks in. This
|
||||
#: can for example be used to open database connections or
|
||||
#: getting hold of the currently logged in user.
|
||||
#: To register a function here, use the :meth:`before_request`
|
||||
#: decorator.
|
||||
#: a dictionary with lists of functions that should be called at the
|
||||
#: beginning of the request. The key of the dictionary is the name of
|
||||
#: the module this function is active for, `None` for all requests.
|
||||
#: This can for example be used to open database connections or
|
||||
#: getting hold of the currently logged in user. To register a
|
||||
#: function here, use the :meth:`before_request` decorator.
|
||||
self.before_request_funcs = {}
|
||||
|
||||
#: a list of functions that are called at the end of the
|
||||
#: request. The function is passed the current response
|
||||
#: object and modify it in place or replace it.
|
||||
#: To register a function here use the :meth:`after_request`
|
||||
#: decorator.
|
||||
#: a dictionary with lists of functions that should be called after
|
||||
#: each request. The key of the dictionary is the name of the module
|
||||
#: this function is active for, `None` for all requests. This can for
|
||||
#: example be used to open database connections or getting hold of the
|
||||
#: currently logged in user. To register a function here, use the
|
||||
#: :meth:`before_request` decorator.
|
||||
self.after_request_funcs = {}
|
||||
|
||||
#: a list of functions that are called without arguments
|
||||
#: to populate the template context. Each returns a dictionary
|
||||
#: that the template context is updated with.
|
||||
#: To register a function here, use the :meth:`context_processor`
|
||||
#: decorator.
|
||||
self.template_context_processors = [_default_template_ctx_processor]
|
||||
#: a dictionary with list of functions that are called without arguments
|
||||
#: to populate the template context. They key of the dictionary is the
|
||||
#: name of the module this function is active for, `None` for all
|
||||
#: requests. Each returns a dictionary that the template context is
|
||||
#: updated with. To register a function here, use the
|
||||
#: :meth:`context_processor` decorator.
|
||||
self.template_context_processors = {
|
||||
None: [_default_template_ctx_processor]
|
||||
}
|
||||
|
||||
#: the :class:`~werkzeug.routing.Map` for this instance. You can use
|
||||
#: this to change the routing converters after the class was created
|
||||
|
|
@ -457,7 +502,7 @@ class Flask(object):
|
|||
self.add_url_rule(self.static_path + '/<filename>',
|
||||
build_only=True, endpoint='static')
|
||||
if pkg_resources is not None:
|
||||
target = (self.package_name, 'static')
|
||||
target = (self.import_name, 'static')
|
||||
else:
|
||||
target = os.path.join(self.root_path, 'static')
|
||||
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
|
||||
|
|
@ -483,7 +528,7 @@ class Flask(object):
|
|||
"""
|
||||
if pkg_resources is None:
|
||||
return FileSystemLoader(os.path.join(self.root_path, 'templates'))
|
||||
return PackageLoader(self.package_name)
|
||||
return PackageLoader(self.import_name)
|
||||
|
||||
def update_template_context(self, context):
|
||||
"""Update the template context with some commonly used variables.
|
||||
|
|
@ -492,7 +537,11 @@ class Flask(object):
|
|||
:param context: the context as a dictionary that is updated in place
|
||||
to add extra variables.
|
||||
"""
|
||||
for func in self.template_context_processors:
|
||||
funcs = self.template_context_processors[None]
|
||||
mod = _request_ctx_stack.top.request.module
|
||||
if mod is not None and mod in self.template_context_processors:
|
||||
funcs = chain(funcs, self.template_context_processors[mod])
|
||||
for func in funcs:
|
||||
context.update(func())
|
||||
|
||||
def run(self, host='127.0.0.1', port=5000, **options):
|
||||
|
|
@ -521,32 +570,6 @@ class Flask(object):
|
|||
from werkzeug import Client
|
||||
return Client(self, self.response_class, use_cookies=True)
|
||||
|
||||
def open_resource(self, resource):
|
||||
"""Opens a resource from the application's resource folder. To see
|
||||
how this works, consider the following folder structure::
|
||||
|
||||
/myapplication.py
|
||||
/schemal.sql
|
||||
/static
|
||||
/style.css
|
||||
/template
|
||||
/layout.html
|
||||
/index.html
|
||||
|
||||
If you want to open the `schema.sql` file you would do the
|
||||
following::
|
||||
|
||||
with app.open_resource('schema.sql') as f:
|
||||
contents = f.read()
|
||||
do_something_with(contents)
|
||||
|
||||
:param resource: the name of the resource. To access resources within
|
||||
subfolders use forward slashes as separator.
|
||||
"""
|
||||
if pkg_resources is None:
|
||||
return open(os.path.join(self.root_path, resource), 'rb')
|
||||
return pkg_resources.resource_stream(self.package_name, resource)
|
||||
|
||||
def open_session(self, request):
|
||||
"""Creates or opens a new session. Default implementation stores all
|
||||
session data in a signed cookie. This requires that the
|
||||
|
|
@ -574,8 +597,8 @@ class Flask(object):
|
|||
"""Registers a module with this application."""
|
||||
options.setdefault('url_prefix', module.url_prefix)
|
||||
state = _ModuleSetupState(self, **options)
|
||||
for func, args in module._register_events:
|
||||
func(state, *args)
|
||||
for func in module._register_events:
|
||||
func(state)
|
||||
|
||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
||||
"""Connects a URL rule. Works exactly like the :meth:`route`
|
||||
|
|
@ -726,7 +749,7 @@ class Flask(object):
|
|||
|
||||
def context_processor(self, f):
|
||||
"""Registers a template context processor function."""
|
||||
self.template_context_processors.append(f)
|
||||
self.template_context_processors[None].append(f)
|
||||
return f
|
||||
|
||||
def dispatch_request(self):
|
||||
|
|
|
|||
|
|
@ -302,7 +302,7 @@ class ModuleTestCase(unittest.TestCase):
|
|||
|
||||
def test_basic_module(self):
|
||||
app = flask.Flask(__name__)
|
||||
admin = flask.Module('admin', url_prefix='/admin')
|
||||
admin = flask.Module(__name__, 'admin', url_prefix='/admin')
|
||||
@admin.route('/')
|
||||
def index():
|
||||
return 'admin index'
|
||||
|
|
@ -325,7 +325,7 @@ class ModuleTestCase(unittest.TestCase):
|
|||
def test_request_processing(self):
|
||||
catched = []
|
||||
app = flask.Flask(__name__)
|
||||
admin = flask.Module('admin', url_prefix='/admin')
|
||||
admin = flask.Module(__name__, 'admin', url_prefix='/admin')
|
||||
@admin.before_request
|
||||
def before_admin_request():
|
||||
catched.append('before-admin')
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue