forked from orbit-oss/flask
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
|
_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):
|
class _ModuleSetupState(object):
|
||||||
|
|
||||||
def __init__(self, app, url_prefix=None):
|
def __init__(self, app, url_prefix=None):
|
||||||
|
|
@ -302,44 +339,53 @@ class _ModuleSetupState(object):
|
||||||
self.url_prefix = url_prefix
|
self.url_prefix = url_prefix
|
||||||
|
|
||||||
|
|
||||||
class Module(object):
|
class Module(_PackageBoundObject):
|
||||||
"""Container object that enables pluggable applications"""
|
"""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.name = name
|
||||||
self.url_prefix = url_prefix
|
self.url_prefix = url_prefix
|
||||||
self._register_events = []
|
self._register_events = []
|
||||||
|
|
||||||
def route(self, rule, **options):
|
def route(self, rule, **options):
|
||||||
|
"""Like :meth:`flask.Flask.route` but for a module"""
|
||||||
def decorator(f):
|
def decorator(f):
|
||||||
self.add_url_rule(rule, f.__name__, f, **options)
|
self.add_url_rule(rule, f.__name__, f, **options)
|
||||||
return f
|
return f
|
||||||
return decorator
|
return decorator
|
||||||
|
|
||||||
def add_url_rule(self, rule, endpoint, view_func=None, **options):
|
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):
|
def before_request(self, f):
|
||||||
|
"""Like :meth:`flask.Flask.before_request` but for a module"""
|
||||||
self._record(lambda s: s.app.before_request_funcs
|
self._record(lambda s: s.app.before_request_funcs
|
||||||
.setdefault(self.name, []).append(f), ())
|
.setdefault(self.name, []).append(f))
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def after_request(self, 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
|
self._record(lambda s: s.app.after_request_funcs
|
||||||
.setdefault(self.name, []).append(f), ())
|
.setdefault(self.name, []).append(f))
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def _record(self, func, args):
|
def _record(self, func):
|
||||||
self._register_events.append((func, args))
|
self._register_events.append(func)
|
||||||
|
|
||||||
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)
|
|
||||||
|
|
||||||
|
|
||||||
class Flask(object):
|
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
|
||||||
application. Once it is created it will act as a central registry for
|
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_']
|
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 debug flag. Set this to `True` to enable debugging of
|
||||||
#: the application. In debug mode the debugger will kick in
|
#: the application. In debug mode the debugger will kick in
|
||||||
#: when an unhandled exception ocurrs and the integrated server
|
#: when an unhandled exception ocurrs and the integrated server
|
||||||
|
|
@ -394,13 +442,6 @@ class Flask(object):
|
||||||
#: code are detected.
|
#: code are detected.
|
||||||
self.debug = False
|
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
|
#: a dictionary of all view functions registered. The keys will
|
||||||
#: be function names which are also used to generate URLs and
|
#: be function names which are also used to generate URLs and
|
||||||
#: the values are the function objects themselves.
|
#: the values are the function objects themselves.
|
||||||
|
|
@ -414,27 +455,31 @@ class Flask(object):
|
||||||
#: decorator.
|
#: decorator.
|
||||||
self.error_handlers = {}
|
self.error_handlers = {}
|
||||||
|
|
||||||
#: a list of functions that should be called at the beginning
|
#: a dictionary with lists of functions that should be called at the
|
||||||
#: of the request before request dispatching kicks in. This
|
#: beginning of the request. The key of the dictionary is the name of
|
||||||
#: can for example be used to open database connections or
|
#: the module this function is active for, `None` for all requests.
|
||||||
#: getting hold of the currently logged in user.
|
#: This can for example be used to open database connections or
|
||||||
#: To register a function here, use the :meth:`before_request`
|
#: getting hold of the currently logged in user. To register a
|
||||||
#: decorator.
|
#: function here, use the :meth:`before_request` decorator.
|
||||||
self.before_request_funcs = {}
|
self.before_request_funcs = {}
|
||||||
|
|
||||||
#: a list of functions that are called at the end of the
|
#: a dictionary with lists of functions that should be called after
|
||||||
#: request. The function is passed the current response
|
#: each request. The key of the dictionary is the name of the module
|
||||||
#: object and modify it in place or replace it.
|
#: this function is active for, `None` for all requests. This can for
|
||||||
#: To register a function here use the :meth:`after_request`
|
#: example be used to open database connections or getting hold of the
|
||||||
#: decorator.
|
#: currently logged in user. To register a function here, use the
|
||||||
|
#: :meth:`before_request` decorator.
|
||||||
self.after_request_funcs = {}
|
self.after_request_funcs = {}
|
||||||
|
|
||||||
#: a list of functions that are called without arguments
|
#: a dictionary with list of functions that are called without arguments
|
||||||
#: to populate the template context. Each returns a dictionary
|
#: to populate the template context. They key of the dictionary is the
|
||||||
#: that the template context is updated with.
|
#: name of the module this function is active for, `None` for all
|
||||||
#: To register a function here, use the :meth:`context_processor`
|
#: requests. Each returns a dictionary that the template context is
|
||||||
#: decorator.
|
#: updated with. To register a function here, use the
|
||||||
self.template_context_processors = [_default_template_ctx_processor]
|
#: :meth:`context_processor` decorator.
|
||||||
|
self.template_context_processors = {
|
||||||
|
None: [_default_template_ctx_processor]
|
||||||
|
}
|
||||||
|
|
||||||
#: the :class:`~werkzeug.routing.Map` for this instance. You can use
|
#: the :class:`~werkzeug.routing.Map` for this instance. You can use
|
||||||
#: this to change the routing converters after the class was created
|
#: 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>',
|
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 pkg_resources is not None:
|
||||||
target = (self.package_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')
|
||||||
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
|
self.wsgi_app = SharedDataMiddleware(self.wsgi_app, {
|
||||||
|
|
@ -483,7 +528,7 @@ class Flask(object):
|
||||||
"""
|
"""
|
||||||
if pkg_resources is None:
|
if 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.package_name)
|
return PackageLoader(self.import_name)
|
||||||
|
|
||||||
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.
|
||||||
|
|
@ -492,7 +537,11 @@ class Flask(object):
|
||||||
:param context: the context as a dictionary that is updated in place
|
:param context: the context as a dictionary that is updated in place
|
||||||
to add extra variables.
|
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())
|
context.update(func())
|
||||||
|
|
||||||
def run(self, host='127.0.0.1', port=5000, **options):
|
def run(self, host='127.0.0.1', port=5000, **options):
|
||||||
|
|
@ -521,32 +570,6 @@ class Flask(object):
|
||||||
from werkzeug import Client
|
from werkzeug import Client
|
||||||
return Client(self, self.response_class, use_cookies=True)
|
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):
|
def open_session(self, request):
|
||||||
"""Creates or opens a new session. Default implementation stores all
|
"""Creates or opens a new session. Default implementation stores all
|
||||||
session data in a signed cookie. This requires that the
|
session data in a signed cookie. This requires that the
|
||||||
|
|
@ -574,8 +597,8 @@ class Flask(object):
|
||||||
"""Registers a module with this application."""
|
"""Registers a module with this application."""
|
||||||
options.setdefault('url_prefix', module.url_prefix)
|
options.setdefault('url_prefix', module.url_prefix)
|
||||||
state = _ModuleSetupState(self, **options)
|
state = _ModuleSetupState(self, **options)
|
||||||
for func, args in module._register_events:
|
for func in module._register_events:
|
||||||
func(state, *args)
|
func(state)
|
||||||
|
|
||||||
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
def add_url_rule(self, rule, endpoint=None, view_func=None, **options):
|
||||||
"""Connects a URL rule. Works exactly like the :meth:`route`
|
"""Connects a URL rule. Works exactly like the :meth:`route`
|
||||||
|
|
@ -726,7 +749,7 @@ class Flask(object):
|
||||||
|
|
||||||
def context_processor(self, f):
|
def context_processor(self, f):
|
||||||
"""Registers a template context processor function."""
|
"""Registers a template context processor function."""
|
||||||
self.template_context_processors.append(f)
|
self.template_context_processors[None].append(f)
|
||||||
return f
|
return f
|
||||||
|
|
||||||
def dispatch_request(self):
|
def dispatch_request(self):
|
||||||
|
|
|
||||||
|
|
@ -302,7 +302,7 @@ class ModuleTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def test_basic_module(self):
|
def test_basic_module(self):
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
admin = flask.Module('admin', url_prefix='/admin')
|
admin = flask.Module(__name__, 'admin', url_prefix='/admin')
|
||||||
@admin.route('/')
|
@admin.route('/')
|
||||||
def index():
|
def index():
|
||||||
return 'admin index'
|
return 'admin index'
|
||||||
|
|
@ -325,7 +325,7 @@ class ModuleTestCase(unittest.TestCase):
|
||||||
def test_request_processing(self):
|
def test_request_processing(self):
|
||||||
catched = []
|
catched = []
|
||||||
app = flask.Flask(__name__)
|
app = flask.Flask(__name__)
|
||||||
admin = flask.Module('admin', url_prefix='/admin')
|
admin = flask.Module(__name__, 'admin', url_prefix='/admin')
|
||||||
@admin.before_request
|
@admin.before_request
|
||||||
def before_admin_request():
|
def before_admin_request():
|
||||||
catched.append('before-admin')
|
catched.append('before-admin')
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue