Mercurial > hg > decoupage
view decoupage/web.py @ 26:f306089d6def
now use contenttransformer to serve files if /transformer in directory configuration; TODO: cache on a per-directory basis
author | k0s <k0scist@gmail.com> |
---|---|
date | Sun, 17 Jan 2010 19:49:04 -0500 |
parents | d5775f3667a2 |
children | 89a72b2fd149 e384d4569ac3 |
line wrap: on
line source
""" decoupage: a view with webob """ import os from contenttransformer.app import FileTypeTransformer from formatters import formatters from genshi.builder import Markup from genshi.template import TemplateLoader from genshi.template.base import TemplateError from martini.config import ConfigMunger from paste.fileapp import FileApp from pkg_resources import resource_filename from pkg_resources import iter_entry_points from webob import Request, Response, exc class Decoupage(object): ### class level variables defaults = { 'auto_reload': 'False', 'configuration': None, 'directory': None, # directory to serve 'cascade': 'True', # whether to cascade configuration 'template': 'index.html', # XXX see below 'template_directories': '' # list of directories to look for templates } def __init__(self, **app_conf): # set defaults from app configuration kw = self.app_conf('decoupage', app_conf) for key in self.defaults: setattr(self, key, kw.get(key, self.defaults[key])) # configure defaults self.auto_reload = self.auto_reload.lower() == 'true' self.cascade = self.cascade.lower() == 'true' self.directory = self.directory.rstrip(os.path.sep) assert os.path.isdir(self.directory) self.template_directories = self.template_directories.split() # no spaces in directory names, for now assert sum([os.path.isdir(directory) for directory in self.template_directories]) == len(self.template_directories) # static file server self.fileserver = FileApp # pluggable index data formatters self.formatters = {} for formatter in iter_entry_points('decoupage.formatters'): try: _formatter = formatter.load() template_dir = resource_filename(formatter.module_name, 'templates') if template_dir not in self.template_directories and os.path.isdir(template_dir): self.template_directories.append(template_dir) except: continue # XXX should probably raise self.formatters[formatter.name] = _formatter # template loader self.loader = TemplateLoader(self.template_directories, auto_reload=self.auto_reload) ### methods dealing with HTTP def __call__(self, environ, start_response): request = Request(environ) filename = request.path_info.strip('/') path = os.path.join(self.directory, filename) if os.path.exists(path): if os.path.isdir(path): if not request.path_info.endswith('/'): raise exc.HTTPMovedPermanently(add_slash=True) res = self.get(request) return res(environ, start_response) else: conf = self.conf(request.path_info.rsplit('/',1)[0]) if '/transformer' in conf: args = [i.split('=', 1) for i in conf['/transformer'].split(',') if '=' in i] fileserver = FileTypeTransformer(*args) else: fileserver = FileApp fileserver = fileserver(path) return fileserver(environ, start_response) else: raise exc.HTTPNotFound() def get(self, request): """ return response to a GET requst """ # ensure a sane path path = request.path_info.strip('/') directory = os.path.join(self.directory, path) path = '/%s' % path # get the configuraton conf = self.conf(path) # build data dictionary files = self.filedata(path, directory, conf) data = {'path': path, 'files': files, 'request': request } data['title'] = conf.get('/title') data['directory'] = directory data['include'] = None data['css'] = () # apply formatters # XXX this should be cached if not self.auto_reload if '/formatters' in conf: # ordered list of formatters to be applied first formatters = [ i for i in conf['/formatters'].split() if i in self.formatters ] else: formatters = [] for key in conf: if key.startswith('/'): key = key[1:] if key in self.formatters and key not in formatters: formatters.append(key) for name in formatters: formatter = self.formatters[name](conf.get('/%s' % name, '')) formatter(request, data) # render the template template = conf.get('/template') local_index = False if template is None: if 'index.html' in [ f['name'] for f in files ]: local_index = os.path.join(directory, 'index.html') template = local_index else: template = self.template else: if not os.path.isabs(template): _template = os.path.join(directory, template) if os.path.exists(_template): template = _template else: for directory in self.template_directories: if template in os.listdir(directory): break else: raise IOError("template %s not found" % template) try: template = self.loader.load(template) res = template.generate(**data).render('html', doctype='html') except TemplateError: if local_index: return self.fileserver(local_index) raise return Response(content_type='text/html', body=res) ### internal methods def filedata(self, path, directory, conf): files = [] for i in os.listdir(directory): files.append({'path' : '%s/%s' % (path.rstrip('/'), i), 'name': i, 'description': conf.get(i.lower(), None)}) return files def conf(self, path, cascade=None): """returns configuration dictionary appropriate to a path""" if cascade is None: cascase = self.cascade directory = os.path.join(self.directory, path.strip('/')) if path.strip('/'): path_tuple = tuple(path.strip('/').split('/')) else: path_tuple = () # return cached configuration if hasattr(self, '_conf') and path_tuple in self._conf: return self._conf[path_tuple] conf = {} # local configuration ini_path = os.path.join(directory, 'index.ini') if os.path.exists(ini_path): _conf = ConfigMunger(ini_path).dict() if len(_conf) == 1: conf = _conf[_conf.keys()[0]].copy() # global configuration if not conf and self.configuration and os.path.exists(self.configuration): conf = ConfigMunger(self.configuration).dict().get('/%s' % path.rstrip('/'), {}) # inherit and cascade configuration inherit_directory = None if '/inherit' in conf: inherit_directory = conf['/inherit'] elif self.cascade and path_tuple: inherit_directory = '/%s' % '/'.join(path_tuple[:-1]) if inherit_directory: parent_configuration = self.conf(inherit_directory) for key, value in parent_configuration.items(): if key.startswith('/') and key not in conf: conf[key] = value # cache configuration if not self.auto_reload: if not hasattr(self, '_conf'): self._conf = {} self._conf[path_tuple] = conf return conf def fmtrs(self, path): formatters = [] for key, value in self.conf(path).items(): if key.startswith('/'): key = key[1:] if key in self.formatters: formatter = self.formatters[key](value) def app_conf(self, keystr, app_conf): keystr += '.' return dict([(key.split(keystr, 1)[-1], value) for key, value in app_conf.items() if key.startswith(keystr) ])