From 618cbb6e3e36b655429f72803d9b20e9e38dcfb2 Mon Sep 17 00:00:00 2001 From: Mark McClain Date: Tue, 4 Jan 2011 10:51:37 -0500 Subject: [PATCH 1/2] adding global context and easier customer renderers + refactor of code --- pecan/pecan.py | 9 +++-- pecan/templating.py | 92 +++++++++++++++++++++++++++++++-------------- 2 files changed, 68 insertions(+), 33 deletions(-) diff --git a/pecan/pecan.py b/pecan/pecan.py index c798810..c2f20b6 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,14 @@ def error_for(field): class Pecan(MonitorableProcess): def __init__(self, root, - renderers = renderers, default_renderer = 'kajiki', template_path = 'templates', - hooks = []): + hooks = [], + custom_renderers = {}, + ): self.root = root - self.renderers = renderers + self.renderers = RendererFactory(custom_renderers) self.default_renderer = default_renderer self.hooks = hooks self.template_path = template_path diff --git a/pecan/templating.py b/pecan/templating.py index 2d685c0..4e35a9a 100644 --- a/pecan/templating.py +++ b/pecan/templating.py @@ -1,26 +1,5 @@ __all__ = ['renderers'] -_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() - # # JSON rendering engine @@ -29,7 +8,7 @@ renderers = RendererFactory() class JsonRenderer(object): content_type = 'application/json' - def __init__(self, path): + def __init__(self, path, context): pass def render(self, template_path, namespace): @@ -44,13 +23,14 @@ class JsonRenderer(object): class GenshiRenderer(object): content_type = 'text/html' - def __init__(self, path): + def __init__(self, path, context): from genshi.template import TemplateLoader self.loader = TemplateLoader([path], auto_reload=True) + self.context = context def render(self, template_path, namespace): tmpl = self.loader.load(template_path) - stream = tmpl.generate(**namespace) + stream = tmpl.generate(**self.context.make_ns(namespace)) return stream.render('html') @@ -61,13 +41,14 @@ class GenshiRenderer(object): class MakoRenderer(object): content_type = 'text/html' - def __init__(self, path): + def __init__(self, path, context): from mako.lookup import TemplateLookup self.loader = TemplateLookup(directories=[path]) + self.context = context def render(self, template_path, namespace): tmpl = self.loader.get_template(template_path) - return tmpl.render(**namespace) + return tmpl.render(**self.context.make_ns(namespace)) # @@ -77,11 +58,64 @@ class MakoRenderer(object): class KajikiRenderer(object): content_type = 'text/html' - def __init__(self, path): + def __init__(self, path, context): from kajiki.loader import FileLoader self.loader = FileLoader(path, reload=True) + self.context = context def render(self, template_path, namespace): Template = self.loader.import_(template_path) - stream = Template(namespace) - return stream.render() \ No newline at end of file + stream = Template(self.context.make_ns(namespace)) + return stream.render() + +# +# Rendering Context +# +class RenderingContext(object): + def __init__(self, globals={}): + self.namespace = dict(globals) + + 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_namespace={}): + self._renderers = {} + self._renderer_classes = { + 'genshi': GenshiRenderer, + 'kajiki': KajikiRenderer, + 'mako' : MakoRenderer, + 'json' : JsonRenderer + } + + self.add_renderers(custom_renderers) + self.context = RenderingContext(extra_namespace) + + def add_renderers(self, custom_dict): + self._renderer_classes.update(custom_dict) + + def create(self, name, template_path): + cls = self._renderer_classes.get(name) + + if cls is None: + return None + else: + return cls(template_path, self.context) + + 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] From 0b8b22d380d49ee86ad6fc31f6764972293cfac7 Mon Sep 17 00:00:00 2001 From: Mark McClain Date: Tue, 4 Jan 2011 15:10:21 -0500 Subject: [PATCH 2/2] changes for templating refactor --- pecan/pecan.py | 11 ++-- pecan/templating.py | 107 +++++++++++++++++++++++---------------- tests/test_base.py | 11 +++- tests/test_templating.py | 28 ++++++++++ 4 files changed, 106 insertions(+), 51 deletions(-) create mode 100644 tests/test_templating.py diff --git a/pecan/pecan.py b/pecan/pecan.py index c2f20b6..fa0d83f 100644 --- a/pecan/pecan.py +++ b/pecan/pecan.py @@ -62,14 +62,15 @@ def error_for(field): class Pecan(MonitorableProcess): def __init__(self, root, - default_renderer = 'kajiki', - template_path = 'templates', - hooks = [], - custom_renderers = {}, + default_renderer = 'mako', + template_path = 'templates', + hooks = [], + custom_renderers = {}, + extra_template_vars = {} ): self.root = root - self.renderers = RendererFactory(custom_renderers) + self.renderers = RendererFactory(custom_renderers, extra_template_vars) self.default_renderer = default_renderer self.hooks = hooks self.template_path = template_path diff --git a/pecan/templating.py b/pecan/templating.py index 4e35a9a..cf6ab7d 100644 --- a/pecan/templating.py +++ b/pecan/templating.py @@ -1,5 +1,6 @@ -__all__ = ['renderers'] +__all__ = ['RendererFactory'] +_builtin_renderers = {} # # JSON rendering engine @@ -8,72 +9,91 @@ __all__ = ['renderers'] class JsonRenderer(object): content_type = 'application/json' - def __init__(self, path, context): + 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, context): - from genshi.template import TemplateLoader - self.loader = TemplateLoader([path], auto_reload=True) - self.context = context + 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(**self.context.make_ns(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, context): - from mako.lookup import TemplateLookup - self.loader = TemplateLookup(directories=[path]) - self.context = context + 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(**self.context.make_ns(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, context): - from kajiki.loader import FileLoader - self.loader = FileLoader(path, reload=True) - self.context = context + 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(self.context.make_ns(namespace)) - return stream.render() + 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 # -# Rendering Context +# Extra Vars Rendering # -class RenderingContext(object): - def __init__(self, globals={}): - self.namespace = dict(globals) +class ExtraNamespace(object): + def __init__(self, extras={}): + self.namespace = dict(extras) def update(self, d): self.namespace.update(d) @@ -91,28 +111,25 @@ class RenderingContext(object): # Rendering Factory # class RendererFactory(object): - def __init__(self, custom_renderers={}, extra_namespace={}): + def __init__(self, custom_renderers={}, extra_vars={}): self._renderers = {} - self._renderer_classes = { - 'genshi': GenshiRenderer, - 'kajiki': KajikiRenderer, - 'mako' : MakoRenderer, - 'json' : JsonRenderer - } - + self._renderer_classes = dict(_builtin_renderers) self.add_renderers(custom_renderers) - self.context = RenderingContext(extra_namespace) + 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.context) + return cls(template_path, self.extra_vars) def get(self, name, template_path): key = name+template_path diff --git a/tests/test_base.py b/tests/test_base.py index 33d97ec..f593f99 100644 --- a/tests/test_base.py +++ b/tests/test_base.py @@ -1,5 +1,6 @@ import os from pecan import Pecan, expose, request, response, redirect, abort +from pecan.templating import _builtin_renderers as builtin_renderers from webtest import TestApp from formencode import Schema, validators @@ -104,6 +105,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'): @@ -120,6 +124,9 @@ class TestEngines(object): assert "

Hello, World!

" in r.body def test_kajiki(self): + if 'kajiki' not in builtin_renderers: + return + class RootController(object): @expose('kajiki:kajiki.html') def index(self, name='Jonathan'): @@ -134,8 +141,10 @@ class TestEngines(object): r = app.get('/index.html?name=World') assert r.status_int == 200 assert "

Hello, World!

" in r.body - + def test_mako(self): + if 'mako' not in builtin_renderers: + return class RootController(object): @expose('mako:mako.html') def index(self, name='Jonathan'): diff --git a/tests/test_templating.py b/tests/test_templating.py new file mode 100644 index 0000000..bfeabcd --- /dev/null +++ b/tests/test_templating.py @@ -0,0 +1,28 @@ +from unittest import TestCase + +from pecan.templating import RendererFactory + +class TestTemplate(TestCase): + def setUp(self): + self.rf = RendererFactory() + + def test_available(self): + self.assertTrue(self.rf.available('json')) + self.assertFalse(self.rf.available('badrenderer')) + + def test_create_bad(self): + self.assertEqual(self.rf.get('doesnotexist', '/'), None) + + def test_extra_vars(self): + extra_vars = self.rf.extra_vars + self.assertEqual(extra_vars.make_ns({}), {}) + + extra_vars.update({'foo': 1}) + self.assertEqual(extra_vars.make_ns({}), {'foo':1}) + + def test_update_extra_vars(self): + extra_vars = self.rf.extra_vars + extra_vars.update({'foo': 1}) + + self.assertEqual(extra_vars.make_ns({'bar':2}), {'foo':1, 'bar':2}) + self.assertEqual(extra_vars.make_ns({'foo':2}), {'foo':2})