Commit 80dd7698 by Chris Jerdonek

Simplified Renderer class: Renderer now uses new Loader class.

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