From d809ac74d19342bcc84e4fe043697709b2001cc0 Mon Sep 17 00:00:00 2001 From: Chris McDonough Date: Tue, 30 Jun 2009 21:02:00 +0000 Subject: - Add a ``reload_resources`` configuration file setting (aka the ``BFG_RELOAD_RESOURCES`` environment variable). When this is set to true, the server never needs to be restarted when moving files between directory resource overrides (esp. for templates currently). - Add a ``reload_all`` configuration file setting (aka the ``BFG_RELOAD_ALL`` environment variable) that implies both ``reload_resources`` and ``reload_templates``. - The ``static`` helper view class now uses a ``PackageURLParser`` in order to allow for the overriding of static resources (CSS / logo files, etc) using the ``resource`` ZCML directive. The ``PackageURLParser`` class was added to a (new) ``static`` module in BFG; it is a subclass of the ``StaticURLParser`` class in ``paste.urlparser``. - The ``repoze.bfg.templating.renderer_from_cache`` function now checks for the ``reload_resources`` setting; if it's true, it does not register a template renderer (it won't use the registry as a template renderer cache). - Add ``pkg_resources`` to the glossary. - Update the "Environment" docs to note the existence of ``reload_resources`` and ``reload_all``. - Use a colon instead of a tab as the separator between package name and relpath to form the "spec" when register a ITemplateRenderer. --- repoze/bfg/static.py | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 repoze/bfg/static.py (limited to 'repoze/bfg/static.py') diff --git a/repoze/bfg/static.py b/repoze/bfg/static.py new file mode 100644 index 000000000..01ce98a30 --- /dev/null +++ b/repoze/bfg/static.py @@ -0,0 +1,78 @@ +import os +import pkg_resources + +from paste.httpheaders import ETAG +from paste.urlparser import StaticURLParser +from paste import httpexceptions +from paste import request + +class PackageURLParser(StaticURLParser): + """ This probably won't work with zipimported resources """ + def __init__(self, package_name, resource_name, root_resource=None, + cache_max_age=None): + self.package_name = package_name + self.resource_name = os.path.normpath(resource_name) + if root_resource is None: + root_resource = self.resource_name + self.root_resource = root_resource + self.cache_max_age = cache_max_age + + def __call__(self, environ, start_response): + path_info = environ.get('PATH_INFO', '') + if not path_info: + return self.add_slash(environ, start_response) + if path_info == '/': + # @@: This should obviously be configurable + filename = 'index.html' + else: + filename = request.path_info_pop(environ) + resource = os.path.normcase(os.path.normpath( + self.resource_name + '/' + filename)) + if ( (self.root_resource is not None) and + (not resource.startswith(self.root_resource)) ): + # Out of bounds + return self.not_found(environ, start_response) + if not pkg_resources.resource_exists(self.package_name, resource): + return self.not_found(environ, start_response) + if pkg_resources.resource_isdir(self.package_name, resource): + # @@: Cache? + child_root = (self.root_resource is not None and + self.root_resource or self.resource_name) + return self.__class__( + self.package_name, resource, root_resource=child_root, + cache_max_age=self.cache_max_age)(environ, start_response) + if (environ.get('PATH_INFO') + and environ.get('PATH_INFO') != '/'): # pragma: no cover + return self.error_extra_path(environ, start_response) + full = pkg_resources.resource_filename(self.package_name, resource) + if_none_match = environ.get('HTTP_IF_NONE_MATCH') + if if_none_match: + mytime = os.stat(full).st_mtime + if str(mytime) == if_none_match: + headers = [] + ETAG.update(headers, mytime) + start_response('304 Not Modified', headers) + return [''] # empty body + + fa = self.make_app(full) + if self.cache_max_age: + fa.cache_control(max_age=self.cache_max_age) + return fa(environ, start_response) + + def not_found(self, environ, start_response, debug_message=None): + comment=('SCRIPT_NAME=%r; PATH_INFO=%r; looking in package %s; ' + 'subdir %s ;debug: %s' % (environ.get('SCRIPT_NAME'), + environ.get('PATH_INFO'), + self.package_name, + self.resource_name, + debug_message or '(none)')) + exc = httpexceptions.HTTPNotFound( + 'The resource at %s could not be found' + % request.construct_url(environ), + comment=comment) + return exc.wsgi_application(environ, start_response) + + def __repr__(self): + return '<%s %s:%s at %s>' % (self.__class__.__name__, self.package_name, + self.root_resource, id(self)) + -- cgit v1.2.3