Refactored static folder registration

This commit is contained in:
Armin Ronacher 2010-07-04 12:48:37 +02:00
parent fedc06c295
commit 15012af700
3 changed files with 60 additions and 19 deletions

View file

@ -10,7 +10,6 @@
""" """
import os import os
import posixpath
from threading import Lock from threading import Lock
from datetime import timedelta, datetime from datetime import timedelta, datetime
from itertools import chain from itertools import chain
@ -21,7 +20,7 @@ from werkzeug.routing import Map, Rule
from werkzeug.exceptions import HTTPException, InternalServerError, NotFound from werkzeug.exceptions import HTTPException, InternalServerError, NotFound
from flask.helpers import _PackageBoundObject, url_for, get_flashed_messages, \ from flask.helpers import _PackageBoundObject, url_for, get_flashed_messages, \
_tojson_filter, send_file _tojson_filter
from flask.wrappers import Request, Response from flask.wrappers import Request, Response
from flask.config import ConfigAttribute, Config from flask.config import ConfigAttribute, Config
from flask.ctx import _default_template_ctx_processor, _RequestContext 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 #: 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 #: 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. #: 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' static_path = '/static'
#: The debug flag. Set this to `True` to enable debugging of the #: The debug flag. Set this to `True` to enable debugging of the
@ -190,8 +192,10 @@ class Flask(_PackageBoundObject):
'SERVER_NAME': None 'SERVER_NAME': None
}) })
def __init__(self, import_name): def __init__(self, import_name, static_path=None):
_PackageBoundObject.__init__(self, import_name) _PackageBoundObject.__init__(self, import_name)
if static_path is not None:
self.static_path = static_path
#: The configuration dictionary as :class:`Config`. This behaves #: The configuration dictionary as :class:`Config`. This behaves
#: exactly like a regular dictionary but supports additional methods #: exactly like a regular dictionary but supports additional methods
@ -258,7 +262,8 @@ class Flask(_PackageBoundObject):
#: app.url_map.converters['list'] = ListConverter #: app.url_map.converters['list'] = ListConverter
self.url_map = Map() 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 + '/<filename>', self.add_url_rule(self.static_path + '/<filename>',
endpoint='static', endpoint='static',
view_func=self.send_static_file) view_func=self.send_static_file)
@ -377,20 +382,6 @@ class Flask(_PackageBoundObject):
options.setdefault('use_debugger', self.debug) options.setdefault('use_debugger', self.debug)
return run_simple(host, port, self, **options) 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): def test_client(self):
"""Creates a test client for this application. For information """Creates a test client for this application. For information
about unit testing head over to :ref:`testing`. about unit testing head over to :ref:`testing`.

View file

@ -11,6 +11,7 @@
import os import os
import sys import sys
import posixpath
import mimetypes import mimetypes
from time import time from time import time
from zlib import adler32 from zlib import adler32
@ -330,6 +331,29 @@ class _PackageBoundObject(object):
#: Where is the app root located? #: Where is the app root located?
self.root_path = _get_package_path(self.import_name) 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): def open_resource(self, resource):
"""Opens a resource from the application's resource folder. To see """Opens a resource from the application's resource folder. To see
how this works, consider the following folder structure:: how this works, consider the following folder structure::

View file

@ -12,6 +12,27 @@
from flask.helpers import _PackageBoundObject 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 + '/<filename>',
'%s.static' % module.name,
view_func=module.send_static_file)
return _register_static
class _ModuleSetupState(object): class _ModuleSetupState(object):
def __init__(self, app, url_prefix=None): def __init__(self, app, url_prefix=None):
@ -67,7 +88,8 @@ class Module(_PackageBoundObject):
:ref:`working-with-modules` section. :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: if name is None:
assert '.' in import_name, 'name required if package name ' \ assert '.' in import_name, 'name required if package name ' \
'does not point to a submodule' 'does not point to a submodule'
@ -77,6 +99,10 @@ class Module(_PackageBoundObject):
self.url_prefix = url_prefix self.url_prefix = url_prefix
self._register_events = [] 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): def route(self, rule, **options):
"""Like :meth:`Flask.route` but for a module. The endpoint for the """Like :meth:`Flask.route` but for a module. The endpoint for the
:func:`url_for` function is prefixed with the name of the module. :func:`url_for` function is prefixed with the name of the module.