Commit 80dd7698 by Chris Jerdonek

Simplified Renderer class: Renderer now uses new Loader class.

parent c756f243
......@@ -11,6 +11,7 @@ import os
import sys
from . import defaults
from .locator import Locator
class Loader(object):
......@@ -78,3 +79,26 @@ class Loader(object):
text = f.read()
return self.unicode(text, encoding)
def load(self, obj, search_dirs):
"""
Find and return the template associated to the given object.
Arguments:
obj: a string or object instance. If obj is a string, then obj
will be interpreted as the template name. Otherwise, obj will
be interpreted as an instance of a user-defined class.
search_dirs: the list of directories in which to search for
templates when loading a template by name.
"""
locator = Locator(extension=self.extension)
if isinstance(obj, basestring):
path = locator.find_path_by_name(search_dirs, obj)
else:
path = locator.find_path_by_object(search_dirs, obj)
return self.read(path)
......@@ -11,9 +11,7 @@ import sys
from . import defaults
from .context import Context
# TODO: remove this alias.
from .loader import Loader as Reader
from .locator import Locator
from .loader import Loader
from .renderengine import RenderEngine
......@@ -43,7 +41,7 @@ class Renderer(object):
"""
def __init__(self, file_encoding=None, default_encoding=None,
decode_errors='strict', search_dirs=None, file_extension=None,
decode_errors=None, search_dirs=None, file_extension=None,
escape=None, partials=None):
"""
Construct an instance.
......@@ -87,9 +85,8 @@ class Renderer(object):
encoding name returned by sys.getdefaultencoding().
decode_errors: the string to pass as the errors argument to the
built-in function unicode() when converting to unicode any
strings of type str encountered during the rendering process.
Defaults to "strict".
built-in function unicode() when converting str strings to
unicode. Defaults to the package default.
search_dirs: the list of directories in which to search for
templates when loading a template by name. Defaults to the
......@@ -101,6 +98,9 @@ class Renderer(object):
Defaults to the package default.
"""
if decode_errors is None:
decode_errors = defaults.DECODE_ERRORS
if default_encoding is None:
default_encoding = sys.getdefaultencoding()
......@@ -169,32 +169,23 @@ class Renderer(object):
# the default_encoding and decode_errors attributes.
return unicode(s, self.default_encoding, self.decode_errors)
def _make_reader(self):
"""
Create a Reader instance using current attributes.
"""
return Reader(encoding=self.file_encoding, decode_errors=self.decode_errors)
def make_locator(self):
def _make_loader(self):
"""
Create a Locator instance using current attributes.
Create a Loader instance using current attributes.
"""
return Locator(extension=self.file_extension)
return Loader(encoding=self.file_encoding, decode_errors=self.decode_errors,
extension=self.file_extension)
def _make_load_template(self):
"""
Return a function that loads a template by name.
"""
reader = self._make_reader()
locator = self.make_locator()
loader = self._make_loader()
def load_template(template_name):
template_path = locator.find_path_by_name(self.search_dirs, template_name)
return reader.read(template_path)
return loader.load(template_name, self.search_dirs)
return load_template
......@@ -237,18 +228,6 @@ class Renderer(object):
escape=self._escape_to_unicode)
return engine
def read(self, path):
"""
Read and return as a unicode string the file contents at path.
This class uses this method whenever it needs to read a template
file. This method uses the file_encoding and decode_errors
attributes.
"""
reader = self._make_reader()
return reader.read(path)
# TODO: add unit tests for this method.
def load_template(self, template_name):
"""
......@@ -258,19 +237,6 @@ class Renderer(object):
load_template = self._make_load_template()
return load_template(template_name)
def get_associated_template(self, obj):
"""
Find and return the template associated with an object.
The function first searches the directory containing the object's
class definition.
"""
locator = self.make_locator()
template_path = locator.find_path_by_object(self.search_dirs, obj)
return self.read(template_path)
def _render_string(self, template, *context, **kwargs):
"""
Render the given template string using the given context.
......@@ -291,8 +257,10 @@ class Renderer(object):
Render the template associated with the given object.
"""
loader = self._make_loader()
template = loader.load(obj, self.search_dirs)
context = [obj] + list(context)
template = self.get_associated_template(obj)
return self._render_string(template, *context, **kwargs)
......@@ -303,7 +271,8 @@ class Renderer(object):
Read the render() docstring for more information.
"""
template = self.read(template_path)
loader = self._make_loader()
template = loader.read(template_path)
return self._render_string(template, *context, **kwargs)
......
......@@ -10,40 +10,39 @@ import sys
import unittest
from .common import AssertStringMixin
# TODO: remove this alias.
from pystache.loader import Loader as Reader
from pystache.loader import Loader
DATA_DIR = 'tests/data'
class ReaderTestCase(unittest.TestCase, AssertStringMixin):
class LoaderTestCase(unittest.TestCase, AssertStringMixin):
def _get_path(self, filename):
return os.path.join(DATA_DIR, filename)
def test_init__decode_errors(self):
# Test the default value.
reader = Reader()
reader = Loader()
self.assertEquals(reader.decode_errors, 'strict')
reader = Reader(decode_errors='replace')
reader = Loader(decode_errors='replace')
self.assertEquals(reader.decode_errors, 'replace')
def test_init__encoding(self):
# Test the default value.
reader = Reader()
reader = Loader()
self.assertEquals(reader.encoding, sys.getdefaultencoding())
reader = Reader(encoding='foo')
reader = Loader(encoding='foo')
self.assertEquals(reader.encoding, 'foo')
def test_init__extension(self):
# Test the default value.
reader = Reader()
reader = Loader()
self.assertEquals(reader.extension, 'mustache')
reader = Reader(extension='foo')
reader = Loader(extension='foo')
self.assertEquals(reader.extension, 'foo')
def test_unicode__basic__input_str(self):
......@@ -51,7 +50,7 @@ class ReaderTestCase(unittest.TestCase, AssertStringMixin):
Test unicode(): default arguments with str input.
"""
reader = Reader()
reader = Loader()
actual = reader.unicode("foo")
self.assertString(actual, u"foo")
......@@ -61,7 +60,7 @@ class ReaderTestCase(unittest.TestCase, AssertStringMixin):
Test unicode(): default arguments with unicode input.
"""
reader = Reader()
reader = Loader()
actual = reader.unicode(u"foo")
self.assertString(actual, u"foo")
......@@ -76,7 +75,7 @@ class ReaderTestCase(unittest.TestCase, AssertStringMixin):
s = UnicodeSubclass(u"foo")
reader = Reader()
reader = Loader()
actual = reader.unicode(s)
self.assertString(actual, u"foo")
......@@ -86,7 +85,7 @@ class ReaderTestCase(unittest.TestCase, AssertStringMixin):
Test unicode(): encoding attribute.
"""
reader = Reader()
reader = Loader()
non_ascii = u'é'.encode('utf-8')
......@@ -100,65 +99,63 @@ class ReaderTestCase(unittest.TestCase, AssertStringMixin):
Test unicode(): encoding argument.
"""
reader = Reader()
reader = Loader()
non_ascii = u'é'.encode('utf-8')
self.assertRaises(UnicodeDecodeError, reader.unicode, non_ascii)
self.assertEquals(reader.unicode(non_ascii, encoding='utf-8'), u'é')
actual = reader.unicode(non_ascii, encoding='utf-8')
self.assertEquals(actual, u'é')
def test_read(self):
"""
Test read().
"""
reader = Reader()
reader = Loader()
path = self._get_path('ascii.mustache')
self.assertEquals(reader.read(path), 'ascii: abc')
def test_read__returns_unicode(self):
"""
Test that read() returns unicode strings.
"""
reader = Reader()
path = self._get_path('ascii.mustache')
contents = reader.read(path)
self.assertEqual(type(contents), unicode)
actual = reader.read(path)
self.assertString(actual, u'ascii: abc')
def test_read__encoding__attribute(self):
"""
Test read(): encoding attribute respected.
"""
reader = Reader()
reader = Loader()
path = self._get_path('non_ascii.mustache')
self.assertRaises(UnicodeDecodeError, reader.read, path)
reader.encoding = 'utf-8'
self.assertEquals(reader.read(path), u'non-ascii: é')
actual = reader.read(path)
self.assertString(actual, u'non-ascii: é')
def test_read__encoding__argument(self):
"""
Test read(): encoding argument respected.
"""
reader = Reader()
reader = Loader()
path = self._get_path('non_ascii.mustache')
self.assertRaises(UnicodeDecodeError, reader.read, path)
self.assertEquals(reader.read(path, encoding='utf-8'), u'non-ascii: é')
actual = reader.read(path, encoding='utf-8')
self.assertString(actual, u'non-ascii: é')
def test_get__decode_errors(self):
"""
Test get(): decode_errors attribute.
"""
reader = Reader()
reader = Loader()
path = self._get_path('non_ascii.mustache')
self.assertRaises(UnicodeDecodeError, reader.read, path)
reader.decode_errors = 'replace'
self.assertEquals(reader.read(path), u'non-ascii: \ufffd\ufffd')
reader.decode_errors = 'ignore'
actual = reader.read(path)
self.assertString(actual, u'non-ascii: ')
......@@ -12,7 +12,7 @@ import unittest
from examples.simple import Simple
from pystache.renderer import Renderer
from pystache.locator import Locator
from pystache.loader import Loader
from .common import get_data_path
from .data.views import SayHello
......@@ -182,76 +182,33 @@ class RendererTestCase(unittest.TestCase):
# U+FFFD is the official Unicode replacement character.
self.assertEquals(renderer.unicode(s), u'd\ufffd\ufffdf')
## Test the read() method.
## Test the _make_loader() method.
def _read(self, renderer, filename):
path = get_data_path(filename)
return renderer.read(path)
def test_read(self):
renderer = Renderer()
actual = self._read(renderer, 'ascii.mustache')
self.assertEquals(actual, 'ascii: abc')
def test_read__returns_unicode(self):
renderer = Renderer()
actual = self._read(renderer, 'ascii.mustache')
self.assertEquals(type(actual), unicode)
def test_read__file_encoding(self):
filename = 'non_ascii.mustache'
renderer = Renderer()
renderer.file_encoding = 'ascii'
self.assertRaises(UnicodeDecodeError, self._read, renderer, filename)
renderer.file_encoding = 'utf-8'
actual = self._read(renderer, filename)
self.assertEquals(actual, u'non-ascii: é')
def test_read__decode_errors(self):
filename = 'non_ascii.mustache'
renderer = Renderer()
self.assertRaises(UnicodeDecodeError, self._read, renderer, filename)
renderer.decode_errors = 'ignore'
actual = self._read(renderer, filename)
self.assertEquals(actual, 'non-ascii: ')
## Test the make_locator() method.
def test_make_locator__return_type(self):
def test__make_loader__return_type(self):
"""
Test that make_locator() returns a Locator.
Test that _make_loader() returns a Loader.
"""
renderer = Renderer()
locator = renderer.make_locator()
loader = renderer._make_loader()
self.assertEquals(type(locator), Locator)
self.assertEquals(type(loader), Loader)
def test_make_locator__file_extension(self):
def test__make_loader__attributes(self):
"""
Test that make_locator() respects the file_extension attribute.
Test that _make_locator() sets all attributes correctly..
"""
renderer = Renderer()
renderer.file_extension = 'foo'
locator = renderer.make_locator()
self.assertEquals(locator.template_extension, 'foo')
# This test is a sanity check. Strictly speaking, it shouldn't
# be necessary based on our tests above.
def test_make_locator__default(self):
renderer = Renderer()
actual = renderer.make_locator()
renderer.decode_errors = 'dec'
renderer.file_encoding = 'enc'
renderer.file_extension = 'ext'
expected = Locator()
loader = renderer._make_loader()
self.assertEquals(type(actual), type(expected))
self.assertEquals(actual.template_extension, expected.template_extension)
self.assertEquals(loader.decode_errors, 'dec')
self.assertEquals(loader.encoding, 'enc')
self.assertEquals(loader.extension, 'ext')
## Test the render() method.
......
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