Mercurial > hg > decoupage
view decoupage/web.py @ 12:9f91acf9874c
* split on space in formatters to be consist with everything else
* break filedata into its own function
author | k0s <k0scist@gmail.com> |
---|---|
date | Fri, 25 Dec 2009 02:59:47 -0500 |
parents | a328cc9d2c74 |
children | ab0c2bb4d23d |
line wrap: on
line source
""" decoupage: a view with webob """ import os from formatters import formatters from genshi.builder import Markup from genshi.template import TemplateLoader 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: fileserver = self.fileserver(path) return fileserver(environ, start_response) else: raise exc.HTTPNotFound() def get_response(self, text, content_type='text/html'): """construct a response to a GET request""" res = Response(content_type=content_type, body=text) return res 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} # 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') if template is None: if 'index.html' in [ f['name'] for f in files ]: template = os.path.join(directory, 'index.html') else: template = self.template if not os.path.isabs(template): template = os.path.join(directory, template) if not os.path.exists(template): template = self.template template = self.loader.load(template) res = template.generate(**data).render('html', doctype='html') return self.get_response(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) ])