Commit e256e732 by Chris Jerdonek

Added a load_template() method to the Renderer class.

parent 98e4f43f
...@@ -16,6 +16,12 @@ from .reader import Reader ...@@ -16,6 +16,12 @@ from .reader import Reader
from .renderengine import RenderEngine from .renderengine import RenderEngine
# The quote=True argument causes double quotes to be escaped,
# but not single quotes:
# http://docs.python.org/library/cgi.html#cgi.escape
DEFAULT_ESCAPE = lambda s: cgi.escape(s, quote=True)
class Renderer(object): class Renderer(object):
""" """
...@@ -35,6 +41,7 @@ class Renderer(object): ...@@ -35,6 +41,7 @@ class Renderer(object):
""" """
# TODO: rename the loader argument to "partials".
def __init__(self, loader=None, file_encoding=None, default_encoding=None, def __init__(self, loader=None, file_encoding=None, default_encoding=None,
decode_errors='strict', search_dirs=None, file_extension=None, decode_errors='strict', search_dirs=None, file_extension=None,
escape=None): escape=None):
...@@ -43,16 +50,17 @@ class Renderer(object): ...@@ -43,16 +50,17 @@ class Renderer(object):
Arguments: Arguments:
loader: the object (e.g. pystache.Loader or dictionary) that will loader: an object (e.g. pystache.Loader or dictionary) for custom
load templates during the rendering process, for example when partial loading during the rendering process.
loading a partial.
The loader should have a get() method that accepts a string The loader should have a get() method that accepts a string
and returns the corresponding template as a string, preferably and returns the corresponding template as a string, preferably
as a unicode string. If there is no template with that name, as a unicode string. If there is no template with that name,
the method should either return None (as dict.get() does) or the method should either return None (as dict.get() does) or
raise an exception. raise an exception.
Defaults to constructing a default Loader, but using the If this argument is None, partial loading takes place using
file_encoding and decode_errors arguments. the normal procedure of reading templates from the file system
using the Loader-related instance attributes (search_dirs,
file_encoding, etc).
escape: the function used to escape variable tag values when escape: the function used to escape variable tag values when
rendering a template. The function should accept a unicode rendering a template. The function should accept a unicode
...@@ -96,10 +104,7 @@ class Renderer(object): ...@@ -96,10 +104,7 @@ class Renderer(object):
default_encoding = sys.getdefaultencoding() default_encoding = sys.getdefaultencoding()
if escape is None: if escape is None:
# The quote=True argument causes double quotes to be escaped, escape = DEFAULT_ESCAPE
# but not single quotes:
# http://docs.python.org/library/cgi.html#cgi.escape
escape = lambda s: cgi.escape(s, quote=True)
# This needs to be after we set the default default_encoding. # This needs to be after we set the default default_encoding.
if file_encoding is None: if file_encoding is None:
...@@ -114,21 +119,12 @@ class Renderer(object): ...@@ -114,21 +119,12 @@ class Renderer(object):
if isinstance(search_dirs, basestring): if isinstance(search_dirs, basestring):
search_dirs = [search_dirs] search_dirs = [search_dirs]
# This needs to be after we set some of the defaults above.
if loader is None:
reader = Reader(encoding=file_encoding, decode_errors=decode_errors)
loader = Loader(reader=reader, search_dirs=search_dirs, extension=file_extension)
self.decode_errors = decode_errors self.decode_errors = decode_errors
self.default_encoding = default_encoding self.default_encoding = default_encoding
self.escape = escape self.escape = escape
self.file_encoding = file_encoding self.file_encoding = file_encoding
self.file_extension = file_extension self.file_extension = file_extension
# TODO: we should not store a loader attribute because the loader # TODO: rename self.loader to self.partials.
# would no longer reflect the current attributes if, say, someone
# changed the search_dirs attribute after instantiation. Instead,
# we should construct the Loader instance each time on the fly,
# as we do with the Reader in the read() method.
self.loader = loader self.loader = loader
self.search_dirs = search_dirs self.search_dirs = search_dirs
...@@ -191,9 +187,39 @@ class Renderer(object): ...@@ -191,9 +187,39 @@ class Renderer(object):
return context return context
def _make_reader(self):
"""
Create a Reader instance using current attributes.
"""
return Reader(encoding=self.file_encoding, decode_errors=self.decode_errors)
def _make_loader(self):
"""
Create a Loader instance using current attributes.
"""
reader = self._make_reader()
loader = Loader(reader=reader, search_dirs=self.search_dirs, extension=self.file_extension)
return loader
def _make_load_partial(self): def _make_load_partial(self):
"""
Return the load_partial function to pass to RenderEngine.__init__().
"""
if self.loader is None:
loader = self._make_loader()
return loader.get
# Otherwise, create a load_partial function from the custom loader
# that satisfies RenderEngine requirements (and that provides a
# nicer exception, etc).
loader = self.loader
def load_partial(name): def load_partial(name):
template = self.loader.get(name) template = loader.get(name)
if template is None: if template is None:
# TODO: make a TemplateNotFoundException type that provides # TODO: make a TemplateNotFoundException type that provides
...@@ -226,9 +252,18 @@ class Renderer(object): ...@@ -226,9 +252,18 @@ class Renderer(object):
attributes. attributes.
""" """
reader = Reader(encoding=self.file_encoding, decode_errors=self.decode_errors) reader = self._make_reader()
return reader.read(path) return reader.read(path)
# TODO: add unit tests for this method.
def load_template(self, template_name):
"""
Load a template by name from the file system.
"""
loader = self._make_loader()
return loader.get(template_name)
def render_path(self, template_path, context=None, **kwargs): def render_path(self, template_path, context=None, **kwargs):
""" """
Render the template at the given path using the given context. Render the template at the given path using the given context.
......
...@@ -76,7 +76,7 @@ class View(object): ...@@ -76,7 +76,7 @@ class View(object):
if not self.template: if not self.template:
template_name = self._get_template_name() template_name = self._get_template_name()
renderer = self._get_renderer() renderer = self._get_renderer()
self.template = renderer.loader.get(template_name) self.template = renderer.load_template(template_name)
return self.template return self.template
......
...@@ -29,8 +29,8 @@ class RendererInitTestCase(unittest.TestCase): ...@@ -29,8 +29,8 @@ class RendererInitTestCase(unittest.TestCase):
""" """
loader = {'foo': 'bar'} loader = {'foo': 'bar'}
r = Renderer(loader=loader) renderer = Renderer(loader=loader)
self.assertEquals(r.loader, {'foo': 'bar'}) self.assertEquals(renderer.loader, {'foo': 'bar'})
def test_loader__default(self): def test_loader__default(self):
""" """
...@@ -40,52 +40,7 @@ class RendererInitTestCase(unittest.TestCase): ...@@ -40,52 +40,7 @@ class RendererInitTestCase(unittest.TestCase):
renderer = Renderer() renderer = Renderer()
actual = renderer.loader actual = renderer.loader
expected = Loader() self.assertTrue(renderer.loader is None)
self.assertEquals(type(actual), type(expected))
self.assertEquals(actual.template_extension, expected.template_extension)
self.assertEquals(actual.search_dirs, expected.search_dirs)
self.assertEquals(actual.reader.__dict__, expected.reader.__dict__)
def test_loader__default__encoding(self):
"""
Test that the default loader inherits the correct encoding.
"""
renderer = Renderer(file_encoding='foo')
reader = renderer.loader.reader
self.assertEquals(reader.encoding, 'foo')
def test_loader__default__decode_errors(self):
"""
Test that the default loader inherits decode_errors.
"""
renderer = Renderer(decode_errors='foo')
reader = renderer.loader.reader
self.assertEquals(reader.decode_errors, 'foo')
def test_loader__default__file_extension(self):
"""
Test that the default loader inherits file_extension.
"""
renderer = Renderer(file_extension='foo')
loader = renderer.loader
self.assertEquals(loader.template_extension, 'foo')
def test_loader__default__search_dirs(self):
"""
Test that the default loader inherits search_dirs.
"""
renderer = Renderer(search_dirs='foo')
loader = renderer.loader
self.assertEquals(loader.search_dirs, ['foo'])
def test_escape__default(self): def test_escape__default(self):
escape = Renderer().escape escape = Renderer().escape
...@@ -264,6 +219,79 @@ class RendererTestCase(unittest.TestCase): ...@@ -264,6 +219,79 @@ class RendererTestCase(unittest.TestCase):
actual = self._read(renderer, filename) actual = self._read(renderer, filename)
self.assertEquals(actual, 'non-ascii: ') self.assertEquals(actual, 'non-ascii: ')
## Test the _make_loader() method.
def test__make_loader__return_type(self):
"""
Test that _make_loader() returns a Loader.
"""
renderer = Renderer()
loader = renderer._make_loader()
self.assertEquals(type(loader), Loader)
def test__make_loader__file_encoding(self):
"""
Test that _make_loader() respects the file_encoding attribute.
"""
renderer = Renderer()
renderer.file_encoding = 'foo'
loader = renderer._make_loader()
self.assertEquals(loader.reader.encoding, 'foo')
def test__make_loader__decode_errors(self):
"""
Test that _make_loader() respects the decode_errors attribute.
"""
renderer = Renderer()
renderer.decode_errors = 'foo'
loader = renderer._make_loader()
self.assertEquals(loader.reader.decode_errors, 'foo')
def test__make_loader__file_extension(self):
"""
Test that _make_loader() respects the file_extension attribute.
"""
renderer = Renderer()
renderer.file_extension = 'foo'
loader = renderer._make_loader()
self.assertEquals(loader.template_extension, 'foo')
def test__make_loader__search_dirs(self):
"""
Test that _make_loader() respects the search_dirs attribute.
"""
renderer = Renderer()
renderer.search_dirs = ['foo']
loader = renderer._make_loader()
self.assertEquals(loader.search_dirs, ['foo'])
# This test is a sanity check. Strictly speaking, it shouldn't
# be necessary based on our tests above.
def test__make_loader__default(self):
renderer = Renderer()
actual = renderer._make_loader()
expected = Loader()
self.assertEquals(type(actual), type(expected))
self.assertEquals(actual.template_extension, expected.template_extension)
self.assertEquals(actual.search_dirs, expected.search_dirs)
self.assertEquals(actual.reader.__dict__, expected.reader.__dict__)
## Test the render() method. ## Test the render() method.
def test_render__return_type(self): def test_render__return_type(self):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment