diff options
| -rw-r--r-- | .gitignore | 2 | ||||
| -rw-r--r-- | pyramid/configuration.py | 3 | ||||
| -rw-r--r-- | pyramid/mako_templating.py | 67 | ||||
| -rw-r--r-- | pyramid/resource.py | 8 | ||||
| -rw-r--r-- | pyramid/tests/fixtures/helloworld.mak | 3 | ||||
| -rw-r--r-- | pyramid/tests/fixtures/helloworld.mako | 3 | ||||
| -rw-r--r-- | pyramid/tests/test_mako_templating.py | 149 | ||||
| -rw-r--r-- | pyramid/tests/test_resource.py | 22 | ||||
| -rw-r--r-- | setup.py | 1 |
9 files changed, 256 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore index 2954d54f3..3c7d474a5 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ *.txt.py .coverage env26 +env24 + diff --git a/pyramid/configuration.py b/pyramid/configuration.py index 3589cbede..d255ff0ca 100644 --- a/pyramid/configuration.py +++ b/pyramid/configuration.py @@ -45,6 +45,7 @@ from pyramid.interfaces import IException from pyramid import chameleon_text from pyramid import chameleon_zpt +from pyramid.mako_templating import renderer_factory as mako_renderer_factory from pyramid import renderers from pyramid.renderers import RendererHelper from pyramid.authorization import ACLAuthorizationPolicy @@ -82,6 +83,8 @@ DEFAULT_PHASH = md5().hexdigest() DEFAULT_RENDERERS = ( ('.pt', chameleon_zpt.renderer_factory), ('.txt', chameleon_text.renderer_factory), + ('.mak', mako_renderer_factory), + ('.mako', mako_renderer_factory), ('json', renderers.json_renderer_factory), ('string', renderers.string_renderer_factory), ) diff --git a/pyramid/mako_templating.py b/pyramid/mako_templating.py new file mode 100644 index 000000000..7c1c7ed7a --- /dev/null +++ b/pyramid/mako_templating.py @@ -0,0 +1,67 @@ +from zope.interface import implements +from zope.interface import Interface + +from mako.lookup import TemplateLookup + +from pyramid.interfaces import ITemplateRenderer +from pyramid.exceptions import ConfigurationError +from pyramid.threadlocal import get_current_registry +from pyramid.settings import get_settings +from pyramid.resource import resolve_resource_spec +from pyramid.resource import abspath_from_resource_spec + +class IMakoLookup(Interface): + pass + +def renderer_factory(path): + registry = get_current_registry() + lookup = registry.queryUtility(IMakoLookup) + if lookup is None: + settings = get_settings() or {} + reload_templates = settings.get('reload_templates', False) + directories = settings.get('mako.directories') + module_directory = settings.get('mako.module_directory') + input_encoding = settings.get('mako.input_encoding', 'utf-8') + if directories is None: + raise ConfigurationError( + 'Mako template used without a lookup path') + directories = directories.splitlines() + directories = [ abspath_from_resource_spec(d) for d in directories ] + lookup = TemplateLookup(directories=directories, + module_directory=module_directory, + input_encoding=input_encoding, + filesystem_checks=reload_templates) + registry.registerUtility(lookup, IMakoLookup) + _, path = resolve_resource_spec(path) + return MakoLookupTemplateRenderer(path, lookup) + +class MakoLookupTemplateRenderer(object): + implements(ITemplateRenderer) + def __init__(self, path, lookup): + self.path = path + self.lookup = lookup + + def implementation(self): + return self.template + + @property + def template(self): + return self.lookup.get_template(self.path) + + def __call__(self, value, system): + context = system.pop('context', None) + if context is not None: + system['_context'] = context + def_name = None + if isinstance(value, tuple): + def_name, value = value + try: + system.update(value) + except (TypeError, ValueError): + raise ValueError('renderer was passed non-dictionary as value') + template = self.template + if def_name is not None: + template = template.get_def(def_name) + result = template.render_unicode(**system) + return result + diff --git a/pyramid/resource.py b/pyramid/resource.py index b2b2e43a1..e1d25d0d4 100644 --- a/pyramid/resource.py +++ b/pyramid/resource.py @@ -198,4 +198,10 @@ def resource_spec_from_abspath(abspath, package): relpath.replace(os.path.sep, '/')) return abspath - +def abspath_from_resource_spec(spec, pname='__main__'): + if pname is None: + return spec + pname, filename = resolve_resource_spec(spec, pname) + if pname is None: + return filename + return pkg_resources.resource_filename(pname, filename) diff --git a/pyramid/tests/fixtures/helloworld.mak b/pyramid/tests/fixtures/helloworld.mak new file mode 100644 index 000000000..efcf791e8 --- /dev/null +++ b/pyramid/tests/fixtures/helloworld.mak @@ -0,0 +1,3 @@ +## -*- coding: utf-8 -*- +<% a, b = 'foo', u'föö' %> +Hello ${u'föö'} diff --git a/pyramid/tests/fixtures/helloworld.mako b/pyramid/tests/fixtures/helloworld.mako new file mode 100644 index 000000000..efcf791e8 --- /dev/null +++ b/pyramid/tests/fixtures/helloworld.mako @@ -0,0 +1,3 @@ +## -*- coding: utf-8 -*- +<% a, b = 'foo', u'föö' %> +Hello ${u'föö'} diff --git a/pyramid/tests/test_mako_templating.py b/pyramid/tests/test_mako_templating.py new file mode 100644 index 000000000..d67bac89c --- /dev/null +++ b/pyramid/tests/test_mako_templating.py @@ -0,0 +1,149 @@ +## come on python gimme some of that sweet, sweet -*- coding: utf-8 -*- + +import unittest + +class Base(object): + def setUp(self): + from pyramid.configuration import Configurator + self.config = Configurator() + self.config.begin() + import os + here = os.path.abspath(os.path.dirname(__file__)) + self.templates_dir = os.path.join(here, 'fixtures') + + def tearDown(self): + self.config.end() + +class Test_renderer_factory(Base, unittest.TestCase): + def _callFUT(self, path): + from pyramid.mako_templating import renderer_factory + return renderer_factory(path) + + def test_no_directories(self): + from pyramid.exceptions import ConfigurationError + self.assertRaises(ConfigurationError, self._callFUT, 'path') + + def test_no_lookup(self): + from pyramid.mako_templating import IMakoLookup + self.config.add_settings({'mako.directories':self.templates_dir}) + renderer = self._callFUT('helloworld.mak') + lookup = self.config.registry.getUtility(IMakoLookup) + self.assertEqual(lookup.directories, [self.templates_dir]) + self.assertEqual(lookup.filesystem_checks, False) + self.assertEqual(renderer.path, 'helloworld.mak') + self.assertEqual(renderer.lookup, lookup) + + def test_composite_directories_path(self): + from pyramid.mako_templating import IMakoLookup + twice = self.templates_dir + '\n' + self.templates_dir + self.config.add_settings({'mako.directories':twice}) + self._callFUT('helloworld.mak') + lookup = self.config.registry.getUtility(IMakoLookup) + self.assertEqual(lookup.directories, [self.templates_dir]*2) + + def test_with_lookup(self): + from pyramid.mako_templating import IMakoLookup + lookup = dict() + self.config.registry.registerUtility(lookup, IMakoLookup) + renderer = self._callFUT('helloworld.mak') + self.assertEqual(renderer.lookup, lookup) + self.assertEqual(renderer.path, 'helloworld.mak') + +class MakoLookupTemplateRendererTests(Base, unittest.TestCase): + def _getTargetClass(self): + from pyramid.mako_templating import MakoLookupTemplateRenderer + return MakoLookupTemplateRenderer + + def _makeOne(self, *arg, **kw): + klass = self._getTargetClass() + return klass(*arg, **kw) + + def test_instance_implements_ITemplate(self): + from zope.interface.verify import verifyObject + from pyramid.interfaces import ITemplateRenderer + verifyObject(ITemplateRenderer, self._makeOne(None, None)) + + def test_class_implements_ITemplate(self): + from zope.interface.verify import verifyClass + from pyramid.interfaces import ITemplateRenderer + verifyClass(ITemplateRenderer, self._getTargetClass()) + + def test_call(self): + lookup = DummyLookup() + instance = self._makeOne('path', lookup) + result = instance({}, {'system':1}) + self.failUnless(isinstance(result, unicode)) + self.assertEqual(result, u'result') + + def test_call_with_system_context(self): + # lame + lookup = DummyLookup() + instance = self._makeOne('path', lookup) + result = instance({}, {'context':1}) + self.failUnless(isinstance(result, unicode)) + self.assertEqual(result, u'result') + self.assertEqual(lookup.values, {'_context':1}) + + def test_call_with_tuple_value(self): + lookup = DummyLookup() + instance = self._makeOne('path', lookup) + result = instance(('fub', {}), {'context':1}) + self.assertEqual(lookup.deffed, 'fub') + self.assertEqual(result, u'result') + self.assertEqual(lookup.values, {'_context':1}) + + def test_call_with_nondict_value(self): + lookup = DummyLookup() + instance = self._makeOne('path', lookup) + self.assertRaises(ValueError, instance, None, {}) + + def test_implementation(self): + lookup = DummyLookup() + instance = self._makeOne('path', lookup) + result = instance.implementation().render_unicode() + self.failUnless(isinstance(result, unicode)) + self.assertEqual(result, u'result') + +class TestIntegration(unittest.TestCase): + def setUp(self): + import pyramid.mako_templating + from pyramid.configuration import Configurator + self.config = Configurator() + self.config.begin() + self.config.add_settings({'mako.directories': + 'pyramid.tests:fixtures'}) + self.config.add_renderer('.mak', + pyramid.mako_templating.renderer_factory) + + def tearDown(self): + self.config.end() + + def test_render(self): + from pyramid.renderers import render + result = render('helloworld.mak', {'a':1}) + self.assertEqual(result, u'\nHello föö\n') + + def test_render_to_response(self): + from pyramid.renderers import render_to_response + result = render_to_response('helloworld.mak', {'a':1}) + self.assertEqual(result.ubody, u'\nHello föö\n') + + def test_get_renderer(self): + from pyramid.renderers import get_renderer + result = get_renderer('helloworld.mak') + self.assertEqual(result.implementation().render_unicode(), + u'\nHello föö\n') + +class DummyLookup(object): + def get_template(self, path): + self.path = path + return self + + def get_def(self, path): + self.deffed = path + return self + + def render_unicode(self, **values): + self.values = values + return u'result' + diff --git a/pyramid/tests/test_resource.py b/pyramid/tests/test_resource.py index f71102de9..79d511dbd 100644 --- a/pyramid/tests/test_resource.py +++ b/pyramid/tests/test_resource.py @@ -401,7 +401,27 @@ class TestFileOverride(unittest.TestCase): o = self._makeOne('foo.pt', 'package', 'bar.pt') result = o('notfound.pt') self.assertEqual(result, None) - + +class Test_abspath_from_resource_spec(unittest.TestCase): + def _callFUT(self, spec, pname='__main__'): + from pyramid.resource import abspath_from_resource_spec + return abspath_from_resource_spec(spec, pname) + + def test_pname_is_None_before_resolve_resource_spec(self): + result = self._callFUT('abc', None) + self.assertEqual(result, 'abc') + + def test_pname_is_None_after_resolve_resource_spec(self): + result = self._callFUT('/abc', '__main__') + self.assertEqual(result, '/abc') + + def test_pkgrelative(self): + import os + here = os.path.dirname(__file__) + path = os.path.abspath(here) + result = self._callFUT('abc', 'pyramid.tests') + self.assertEqual(result, os.path.join(path, 'abc')) + class DummyOverride: def __init__(self, result): self.result = result @@ -29,6 +29,7 @@ except IOError: install_requires=[ 'Chameleon >= 1.2.3', + 'Mako', 'Paste > 1.7', # temp version pin to prevent PyPi install failure :-( 'PasteDeploy', 'PasteScript', |
