diff --git a/pecan/pecan.py b/pecan/pecan.py index c8999a3..143b091 100644 --- a/pecan/pecan.py +++ b/pecan/pecan.py @@ -1,6 +1,6 @@ from configuration import _runtime_conf from monitor import MonitorableProcess -from templating import renderers +from templating import RendererFactory from routing import lookup_controller from webob import Request, Response, exc @@ -62,13 +62,15 @@ def error_for(field): class Pecan(MonitorableProcess): def __init__(self, root, - renderers = renderers, - default_renderer = 'kajiki', - template_path = 'templates', - hooks = []): + default_renderer = 'mako', + template_path = 'templates', + hooks = [], + custom_renderers = {}, + extra_template_vars = {} + ): self.root = root - self.renderers = renderers + self.renderers = RendererFactory(custom_renderers, extra_template_vars) self.default_renderer = default_renderer self.hooks = hooks self.template_path = template_path @@ -289,4 +291,4 @@ class Pecan(MonitorableProcess): del state.response del state.hooks if hasattr(state, 'controller'): - del state.controller \ No newline at end of file + del state.controller diff --git a/pecan/templating.py b/pecan/templating.py index 2d685c0..cf6ab7d 100644 --- a/pecan/templating.py +++ b/pecan/templating.py @@ -1,26 +1,6 @@ -__all__ = ['renderers'] +__all__ = ['RendererFactory'] -_renderers = {} - -class RendererFactory(object): - def create(self, name, template_path): - if name == 'genshi': - return GenshiRenderer(template_path) - elif name == 'kajiki': - return KajikiRenderer(template_path) - elif name == 'mako': - return MakoRenderer(template_path) - elif name == 'json': - return JsonRenderer(template_path) - - def get(self, name, template_path): - key = name+template_path - if key not in _renderers: - _renderers[key] = self.create(name, template_path) - return _renderers[key] - -renderers = RendererFactory() - +_builtin_renderers = {} # # JSON rendering engine @@ -29,59 +9,130 @@ renderers = RendererFactory() class JsonRenderer(object): content_type = 'application/json' - def __init__(self, path): + def __init__(self, path, extra_vars): pass def render(self, template_path, namespace): from jsonify import encode return encode(namespace) +_builtin_renderers['json'] = JsonRenderer + # # Genshi rendering engine # -class GenshiRenderer(object): - content_type = 'text/html' +try: + from genshi.template import TemplateLoader - def __init__(self, path): - from genshi.template import TemplateLoader - self.loader = TemplateLoader([path], auto_reload=True) + class GenshiRenderer(object): + content_type = 'text/html' + + def __init__(self, path, extra_vars): + self.loader = TemplateLoader([path], auto_reload=True) + self.extra_vars = extra_vars - def render(self, template_path, namespace): - tmpl = self.loader.load(template_path) - stream = tmpl.generate(**namespace) - return stream.render('html') + def render(self, template_path, namespace): + tmpl = self.loader.load(template_path) + stream = tmpl.generate(**self.extra_vars.make_ns(namespace)) + return stream.render('html') + + _builtin_renderers['genshi'] = GenshiRenderer +except ImportError: #pragma no cover + pass # # Mako rendering engine # -class MakoRenderer(object): - content_type = 'text/html' +try: + from mako.lookup import TemplateLookup - def __init__(self, path): - from mako.lookup import TemplateLookup - self.loader = TemplateLookup(directories=[path]) + class MakoRenderer(object): + content_type = 'text/html' + + def __init__(self, path, extra_vars): + self.loader = TemplateLookup(directories=[path]) + self.extra_vars = extra_vars - def render(self, template_path, namespace): - tmpl = self.loader.get_template(template_path) - return tmpl.render(**namespace) + def render(self, template_path, namespace): + tmpl = self.loader.get_template(template_path) + return tmpl.render(**self.extra_vars.make_ns(namespace)) + + _builtin_renderers['mako'] = MakoRenderer +except ImportError: # pragma no cover + pass # # Kajiki rendering engine # -class KajikiRenderer(object): - content_type = 'text/html' +try: + from kajiki.loader import FileLoader - def __init__(self, path): - from kajiki.loader import FileLoader - self.loader = FileLoader(path, reload=True) + class KajikiRenderer(object): + content_type = 'text/html' + + def __init__(self, path, extra_vars): + self.loader = FileLoader(path, reload=True) + self.extra_vars = extra_vars - def render(self, template_path, namespace): - Template = self.loader.import_(template_path) - stream = Template(namespace) - return stream.render() \ No newline at end of file + def render(self, template_path, namespace): + Template = self.loader.import_(template_path) + stream = Template(self.extra_vars.make_ns(namespace)) + return stream.render() + _builtin_renderers['kajiki'] = KajikiRenderer +except ImportError: # pragma no cover + pass + +# +# Extra Vars Rendering +# +class ExtraNamespace(object): + def __init__(self, extras={}): + self.namespace = dict(extras) + + def update(self, d): + self.namespace.update(d) + + def make_ns(self, ns): + if self.namespace: + retval = {} + retval.update(self.namespace) + retval.update(ns) + return retval + else: + return ns + +# +# Rendering Factory +# +class RendererFactory(object): + def __init__(self, custom_renderers={}, extra_vars={}): + self._renderers = {} + self._renderer_classes = dict(_builtin_renderers) + self.add_renderers(custom_renderers) + self.extra_vars = ExtraNamespace(extra_vars) + + def add_renderers(self, custom_dict): + self._renderer_classes.update(custom_dict) + + def available(self, name): + return name in self._renderer_classes + + def create(self, name, template_path): + cls = self._renderer_classes.get(name) + + if cls is None: + return None + else: + return cls(template_path, self.extra_vars) + + def get(self, name, template_path): + key = name+template_path + if key not in self._renderers: + self._renderers[key] = self.create(name, template_path) + return self._renderers[key] diff --git a/tests/test_base.py b/tests/test_base.py index a713bd4..4cd04c8 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,5 +1,7 @@ import os from pecan import Pecan, expose, request, redirect, abort +from pecan import Pecan, expose, request, redirect, abort +from pecan.templating import _builtin_renderers as builtin_renderers from webtest import TestApp from formencode import Schema, validators @@ -122,6 +124,9 @@ class TestEngines(object): template_path = os.path.join(os.path.dirname(__file__), 'templates') def test_genshi(self): + if 'genshi' not in builtin_renderers: + return + class RootController(object): @expose('genshi:genshi.html') def index(self, name='Jonathan'): @@ -138,6 +143,9 @@ class TestEngines(object): assert "