Source code for ew.render

import os
import re
import json
import string
import logging
from cgi import escape

import pkg_resources
from webhelpers.html import literal

from . import errors
from .core import widget_context
from .utils import push_context, LazyProperty

DEFAULT_TEMPLATE_ENGINE='core-ew'

log = logging.getLogger(__name__)

class _Renderer(object):

    def __init__(self, text='', engine=None):
        if engine is None: engine = DEFAULT_TEMPLATE_ENGINE
        self._text = text
        self._engine = engine

    def __call__(self, context):
        return self.engine.render(self.template, context)

    @LazyProperty
    def engine(self):
        return TemplateEngine.get_engine(self._engine)

    @LazyProperty
    def template(self): # pragma no cover
        raise NotImplementedError, 'template'

[docs]class Snippet(_Renderer): @LazyProperty def template(self): return self.engine.parse(self._text)
[docs]class File(_Renderer): @LazyProperty def template(self): return self.engine.load(self._text)
[docs]class TemplateEngine(object): _engines = None variable_providers = [] def __init__(self, entry_point, config): self.name = entry_point.name self.config = config @classmethod
[docs] def initialize(cls, config): cls._engines = {} for ep in pkg_resources.iter_entry_points('easy_widgets.engines'): log.debug('Loading template engine %s', ep.name) config_prefix = ep.name + '.' ep_config = dict( (k[len(config_prefix):], v) for k,v in config.iteritems() if k.startswith(config_prefix)) try: cls._engines[ep.name] = ep.load()(ep, ep_config) except errors.EngineNotFound: # pragma no cover log.debug('Error loading engine %s', ep.name) except: log.exception('Error loading engine %s', ep.name)
@classmethod
[docs] def get_engine(cls, name): if cls._engines is None: cls.initialize({}) return cls._engines.get(name)
@classmethod
[docs] def register_variable_provider(cls, provider): '''Plugin point to allow functions to manipulate the context passed to *all* templates for *all* widgets. def provider(context): ... TemplateEngine.register_variable_provider(provider) ''' cls.variable_providers.append(provider)
[docs] def load(self, template_name): # pragma no cover raise NotImplementedError, 'load'
[docs] def parse(self, template_text): # pragma no cover raise NotImplementedError, 'parse'
[docs] def render(self, template, context): # pragma no cover raise NotImplementedError, 'load'
[docs] def context(self, user_context): context = dict(user_context) context.setdefault('widget', widget_context.widget) for provider in self.variable_providers: provider(context) return context
[docs]class JsonEngine(TemplateEngine):
[docs] def load(self, template_name): # pragma no cover return None
[docs] def parse(self, template_text, filepath=None): return None
[docs] def render(self, template, context): return json.dumps(context)
[docs]class CoreEngine(TemplateEngine): '''Simple extension of string.Template allowing for expressions in ${...}'''
[docs] class ExprDict(dict): re_escape = re.compile(r'&|<|>')
[docs] def get(self, k, *args): try: return self[k] except KeyError: if args: return args[0] raise
def __getitem__(self, k): try: return eval(k, dict(self)) except KeyError: raise except Exception, ex: return '[Exception in %s: %s]' % (k, ex)
[docs] class ExprTemplate(string.Template): idpattern = r'[_a-z][^}]*'
def __init__(self, entry_point, config): super(CoreEngine, self).__init__(entry_point, config) self._dotted_filename_finder = _DottedFilenameFinder()
[docs] def load(self, template_name): try: filepath = self._dotted_filename_finder.get_dotted_filename( template_name, template_extension='.html') return self.parse(open(filepath).read(), filepath) except IOError: raise errors.TemplateNotFound, '%s not found at %s' % ( template_name, filepath)
[docs] def parse(self, template_text, filepath=None): return self.ExprTemplate(unicode(template_text))
[docs] def render(self, template, context): context = self.context(context) context = self.ExprDict(context) return literal(template.safe_substitute(context))
[docs]class Jinja2Engine(TemplateEngine): def __init__(self, entry_point, config): try: import jinja2 except ImportError: # pragma no cover raise errors.EngineNotFound, 'jinja2' self.jinja2 = jinja2 super(Jinja2Engine, self).__init__(entry_point, config) self._dotted_filename_finder = _DottedFilenameFinder() class Loader(jinja2.BaseLoader): def get_source(self_, environment, template): return self.get_source(template) self._environ=jinja2.Environment( loader=Loader(), **config)
[docs] def get_source(self, template_name): filepath = self._dotted_filename_finder.get_dotted_filename( template_name, template_extension='.html') if not os.path.exists(filepath): raise self.jinja2.TemplateNotFound(template_name) mtime = os.path.getmtime(filepath) with open(filepath) as fp: source = fp.read().decode('utf-8') return source, filepath, lambda: mtime==os.path.getmtime(filepath)
[docs] def load(self, template_name): try: return self._environ.get_template(template_name) except self.jinja2.TemplateNotFound: raise errors.TemplateNotFound, '%s not found' % template_name
[docs] def parse(self, template_text, filepath=None): return self._environ.from_string(template_text)
[docs] def render(self, template, context): context = self.context(context) with push_context(widget_context, render_context=context): text = template.render(**context) return literal(text)
[docs]class GenshiEngine(TemplateEngine): def __init__(self, entry_point, config): try: import genshi.template except ImportError: # pragma no cover raise errors.EngineNotFound, 'genshi' self.genshi = genshi super(GenshiEngine, self).__init__(entry_point, config) self._dotted_filename_finder = _DottedFilenameFinder() self._loader = self.genshi.template.TemplateLoader( auto_reload=config.get('auto_reload', True), max_cache_size=config.get('max_cache_size', 100) ) self.mode = config.get('mode', 'html')
[docs] def load(self, template_name): try: filepath = self._dotted_filename_finder.get_dotted_filename( template_name, template_extension='.html') return self._loader.load(filepath) except self.genshi.template.TemplateNotFound: raise errors.TemplateNotFound, '%s not found at %s' % ( template_name, filepath)
[docs] def parse(self, template_text, filepath=None): return self.genshi.template.MarkupTemplate( template_text, filepath=filepath, loader=self._loader)
[docs] def render(self, template, context): context = self.context(context) with push_context(widget_context, render_context=context): stream = template.generate(**context) text = stream.render(self.mode) return literal(text)
[docs]class KajikiEngine(TemplateEngine): force_mode=None def __init__(self, entry_point, config): try: import kajiki except ImportError: # pragma no cover raise errors.EngineNotFound, 'kajiki' super(KajikiEngine, self).__init__(entry_point, config) self.kajiki = kajiki self._loader = kajiki.PackageLoader( reload=config.get('reload', True), force_mode=config.get('force_mode', self.force_mode), )
[docs] def load(self, template_name): try: return self._loader.load(template_name) except IOError: raise errors.TemplateNotFound, template_name
[docs] def parse(self, template_text, filepath=None): if self.force_mode is None: return self.kajiki.XMLTemplate( template_text, filename=filepath) else: return self.kajiki.XMLTemplate( template_text, filename=filepath, mode=self.force_mode)
[docs] def render(self, template, context): context = self.context(context) with push_context(widget_context, render_context=context): return literal(template(context).render())
[docs]class KajikiTextEngine(KajikiEngine): force_mode='text'
[docs] def parse(self, template_text, filepath=None): return self.kajiki.TextTemplate( template_text, filename=filepath)
[docs]class KajikiHTML4Engine(KajikiEngine): force_mode='html'
[docs]class KajikiHTML5Engine(KajikiEngine): force_mode='html5'
[docs]class KajikiXMLEngine(KajikiEngine): force_mode='xml'
class _DottedFilenameFinder(object): def __init__(self): self.cache = {} def get_dotted_filename(self, name, template_extension): result = self.cache.get(name, None) if result is not None: return result package, template = name.rsplit('.', 1) basename = template + template_extension try: result = pkg_resources.resource_filename(package, basename) except ImportError, e: e.args = ( e.args[0] + '. Perhaps you have forgotten an __init__.py in that folder.',) raise self.cache[name] = result return result