summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--pyramid/configuration.py3
-rw-r--r--pyramid/mako_templating.py67
-rw-r--r--pyramid/resource.py8
-rw-r--r--pyramid/tests/fixtures/helloworld.mak3
-rw-r--r--pyramid/tests/fixtures/helloworld.mako3
-rw-r--r--pyramid/tests/test_mako_templating.py149
-rw-r--r--pyramid/tests/test_resource.py22
-rw-r--r--setup.py1
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
diff --git a/setup.py b/setup.py
index ceac5f9b2..44fc10058 100644
--- a/setup.py
+++ b/setup.py
@@ -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',