diff --git a/flask/app.py b/flask/app.py index c902f276..a818ae62 100644 --- a/flask/app.py +++ b/flask/app.py @@ -10,7 +10,6 @@ """ import os -import posixpath from threading import Lock from datetime import timedelta, datetime from itertools import chain @@ -21,7 +20,7 @@ from werkzeug.routing import Map, Rule from werkzeug.exceptions import HTTPException, InternalServerError, NotFound from flask.helpers import _PackageBoundObject, url_for, get_flashed_messages, \ - _tojson_filter, send_file + _tojson_filter from flask.wrappers import Request, Response from flask.config import ConfigAttribute, Config from flask.ctx import _default_template_ctx_processor, _RequestContext @@ -101,6 +100,9 @@ class Flask(_PackageBoundObject): #: Path for the static files. If you don't want to use static files #: you can set this value to `None` in which case no URL rule is added #: and the development server will no longer serve any static files. + #: + #: This is the default used for application and modules unless a + #: different value is passed to the constructor. static_path = '/static' #: The debug flag. Set this to `True` to enable debugging of the @@ -190,8 +192,10 @@ class Flask(_PackageBoundObject): 'SERVER_NAME': None }) - def __init__(self, import_name): + def __init__(self, import_name, static_path=None): _PackageBoundObject.__init__(self, import_name) + if static_path is not None: + self.static_path = static_path #: The configuration dictionary as :class:`Config`. This behaves #: exactly like a regular dictionary but supports additional methods @@ -258,7 +262,8 @@ class Flask(_PackageBoundObject): #: app.url_map.converters['list'] = ListConverter self.url_map = Map() - if self.static_path is not None: + # if there is a static folder, register it for the application. + if self.has_static_folder: self.add_url_rule(self.static_path + '/', endpoint='static', view_func=self.send_static_file) @@ -377,20 +382,6 @@ class Flask(_PackageBoundObject): options.setdefault('use_debugger', self.debug) return run_simple(host, port, self, **options) - def send_static_file(self, filename): - """Function used internally to send static files from the static - folder to the browser. - - .. versionadded:: 0.5 - """ - filename = posixpath.normpath(filename) - if filename.startswith('../'): - raise NotFound() - filename = os.path.join(self.root_path, 'static', filename) - if not os.path.isfile(filename): - raise NotFound() - return send_file(filename, conditional=True) - def test_client(self): """Creates a test client for this application. For information about unit testing head over to :ref:`testing`. diff --git a/flask/helpers.py b/flask/helpers.py index 0afd4a85..518d953f 100644 --- a/flask/helpers.py +++ b/flask/helpers.py @@ -11,6 +11,7 @@ import os import sys +import posixpath import mimetypes from time import time from zlib import adler32 @@ -330,6 +331,29 @@ class _PackageBoundObject(object): #: Where is the app root located? self.root_path = _get_package_path(self.import_name) + @property + def has_static_folder(self): + """This is `True` if the package bound object's container has a + folder named ``'static'``. + + .. versionadded:: 0.5 + """ + return os.path.isdir(os.path.join(self.root_path, 'static')) + + def send_static_file(self, filename): + """Function used internally to send static files from the static + folder to the browser. + + .. versionadded:: 0.5 + """ + filename = posixpath.normpath(filename) + if filename.startswith('../'): + raise NotFound() + filename = os.path.join(self.root_path, 'static', filename) + if not os.path.isfile(filename): + raise NotFound() + return send_file(filename, conditional=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:: diff --git a/flask/module.py b/flask/module.py index 85898511..cc904d20 100644 --- a/flask/module.py +++ b/flask/module.py @@ -12,6 +12,27 @@ from flask.helpers import _PackageBoundObject +def _register_module_static(module): + """Internal helper function that returns a function for recording + that registers the `send_static_file` function for the module on + the application of necessary. + """ + def _register_static(state): + # 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 + '/', + '%s.static' % module.name, + view_func=module.send_static_file) + return _register_static + + class _ModuleSetupState(object): def __init__(self, app, url_prefix=None): @@ -67,7 +88,8 @@ class Module(_PackageBoundObject): :ref:`working-with-modules` section. """ - def __init__(self, import_name, name=None, url_prefix=None): + 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' @@ -77,6 +99,10 @@ class Module(_PackageBoundObject): self.url_prefix = url_prefix self._register_events = [] + # if there is a static folder, register it for this module + if self.has_static_folder: + self._record(_register_module_static(self)) + 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.