Commit 58494244 by Chris Jerdonek

Merge 'issue_72' into development: closing issue #73 (Reader class)

Created a Reader class to encapsulate reading files with the encoding
and decode_errors attributes.
parents b2f59598 d9769c86
......@@ -8,13 +8,15 @@ This module provides a Loader class.
import os
import sys
DEFAULT_DECODE_ERRORS = 'strict'
from .reader import Reader
DEFAULT_EXTENSION = 'mustache'
class Loader(object):
def __init__(self, search_dirs=None, extension=None, encoding=None, decode_errors=None):
def __init__(self, search_dirs=None, extension=None, reader=None):
"""
Construct a template loader.
......@@ -28,21 +30,13 @@ class Loader(object):
extension: the template file extension. Defaults to "mustache".
Pass False for no extension (i.e. extensionless template files).
encoding: the name of the encoding to use when converting file
contents to unicode. This name will be passed as the encoding
argument to the built-in function unicode(). Defaults to the
encoding name returned by sys.getdefaultencoding().
decode_errors: the string to pass as the "errors" argument to the
built-in function unicode() when converting file contents to
unicode. Defaults to "strict".
reader: the Reader instance to use to read file contents and
return them as unicode strings. Defaults to constructing
the default Reader with no constructor arguments.
"""
if decode_errors is None:
decode_errors = DEFAULT_DECODE_ERRORS
if encoding is None:
encoding = sys.getdefaultencoding()
if reader is None:
reader = Reader()
if extension is None:
extension = DEFAULT_EXTENSION
......@@ -53,11 +47,17 @@ class Loader(object):
if isinstance(search_dirs, basestring):
search_dirs = [search_dirs]
self.decode_errors = decode_errors
self.reader = reader
self.search_dirs = search_dirs
self.template_encoding = encoding
self.template_extension = extension
def _read(self, path):
"""
Read and return a template as a unicode string.
"""
return self.reader.read(path)
def make_file_name(self, template_name):
file_name = template_name
if self.template_extension is not False:
......@@ -79,23 +79,8 @@ class Loader(object):
for dir_path in search_dirs:
file_path = os.path.join(dir_path, file_name)
if os.path.exists(file_path):
return self._load_template_file(file_path)
return self._read(file_path)
# TODO: we should probably raise an exception of our own type.
raise IOError('"%s" not found in "%s"' % (template_name, ':'.join(search_dirs),))
def _load_template_file(self, file_path):
"""
Read a template file, and return it as a string.
"""
f = open(file_path, 'r')
try:
template = f.read()
finally:
f.close()
template = unicode(template, self.template_encoding, self.decode_errors)
return template
# coding: utf-8
"""
This module provides a Reader class to read a template given a path.
"""
from __future__ import with_statement
import os
import sys
DEFAULT_DECODE_ERRORS = 'strict'
class Reader(object):
def __init__(self, encoding=None, decode_errors=None):
"""
Construct a template reader.
Arguments:
encoding: the file encoding. This is the name of the encoding to
use when converting file contents to unicode. This name is
passed as the encoding argument to Python's built-in function
unicode(). Defaults to the encoding name returned by
sys.getdefaultencoding().
decode_errors: the string to pass as the errors argument to the
built-in function unicode() when converting file contents to
unicode. Defaults to "strict".
"""
if decode_errors is None:
decode_errors = DEFAULT_DECODE_ERRORS
if encoding is None:
encoding = sys.getdefaultencoding()
self.decode_errors = decode_errors
self.encoding = encoding
def read(self, path):
"""
Read the template at the given path, and return it as a unicode string.
"""
with open(path, 'r') as f:
text = f.read()
text = unicode(text, self.encoding, self.decode_errors)
return text
......@@ -10,6 +10,7 @@ import sys
from .context import Context
from .loader import Loader
from .reader import Reader
from .renderengine import RenderEngine
......@@ -47,9 +48,8 @@ class Renderer(object):
as a unicode string. If there is no template with that name,
the method should either return None (as dict.get() does) or
raise an exception.
Defaults to constructing a Loader instance with
default_encoding and decode_errors passed as the encoding and
decode_errors arguments, respectively.
Defaults to constructing a default Loader, but using the
default_encoding and decode_errors arguments.
escape: the function used to escape variable tag values when
rendering a template. The function should accept a unicode
......@@ -86,7 +86,8 @@ class Renderer(object):
escape = lambda s: cgi.escape(s, quote=True)
if loader is None:
loader = Loader(encoding=default_encoding, decode_errors=decode_errors)
reader = Reader(encoding=default_encoding, decode_errors=decode_errors)
loader = Loader(reader=reader)
self.decode_errors = decode_errors
self.default_encoding = default_encoding
......
......@@ -10,6 +10,7 @@ from types import UnboundMethodType
from .context import Context
from .loader import Loader
from .reader import Reader
from .renderer import Renderer
......@@ -57,7 +58,8 @@ class View(object):
# user did not supply a load_template to the constructor)
# to let users set the template_extension attribute, etc. after
# View.__init__() has already been called.
loader = Loader(search_dirs=self.template_path, encoding=self.template_encoding,
reader = Reader(encoding=self.template_encoding)
loader = Loader(search_dirs=self.template_path, reader=reader,
extension=self.template_extension)
self._loader = loader
......
......@@ -5,6 +5,7 @@ import sys
import unittest
from pystache.loader import Loader
from pystache.reader import Reader
DATA_DIR = 'tests/data'
......@@ -23,22 +24,6 @@ class LoaderTestCase(unittest.TestCase):
loader = Loader(search_dirs=['foo'])
self.assertEquals(loader.search_dirs, ['foo'])
def test_init__decode_errors(self):
# Test the default value.
loader = Loader()
self.assertEquals(loader.decode_errors, 'strict')
loader = Loader(decode_errors='replace')
self.assertEquals(loader.decode_errors, 'replace')
def test_init__encoding(self):
# Test the default value.
loader = Loader()
self.assertEquals(loader.template_encoding, sys.getdefaultencoding())
loader = Loader(encoding='foo')
self.assertEquals(loader.template_encoding, 'foo')
def test_init__extension(self):
# Test the default value.
loader = Loader()
......@@ -50,6 +35,17 @@ class LoaderTestCase(unittest.TestCase):
loader = Loader(extension=False)
self.assertTrue(loader.template_extension is False)
def test_init__reader(self):
# Test the default value.
loader = Loader()
reader = loader.reader
self.assertEquals(reader.encoding, sys.getdefaultencoding())
self.assertEquals(reader.decode_errors, 'strict')
reader = Reader()
loader = Loader(reader=reader)
self.assertTrue(loader.reader is reader)
def test_make_file_name(self):
loader = Loader()
......@@ -103,25 +99,3 @@ class LoaderTestCase(unittest.TestCase):
actual = loader.get('ascii')
self.assertEqual(type(actual), unicode)
def test_get__encoding(self):
"""
Test get(): encoding attribute respected.
"""
loader = self._loader()
self.assertRaises(UnicodeDecodeError, loader.get, 'nonascii')
loader.template_encoding = 'utf-8'
self.assertEquals(loader.get('nonascii'), u'non-ascii: é')
def test_get__decode_errors(self):
"""
Test get(): decode_errors attribute.
"""
loader = self._loader()
self.assertRaises(UnicodeDecodeError, loader.get, 'nonascii')
loader.decode_errors = 'replace'
self.assertEquals(loader.get('nonascii'), u'non-ascii: \ufffd\ufffd')
# encoding: utf-8
"""
Unit tests of reader.py.
"""
import os
import sys
import unittest
from pystache.reader import Reader
DATA_DIR = 'tests/data'
class ReaderTestCase(unittest.TestCase):
def _get_path(self, filename):
return os.path.join(DATA_DIR, filename)
def test_init__decode_errors(self):
# Test the default value.
reader = Reader()
self.assertEquals(reader.decode_errors, 'strict')
reader = Reader(decode_errors='replace')
self.assertEquals(reader.decode_errors, 'replace')
def test_init__encoding(self):
# Test the default value.
reader = Reader()
self.assertEquals(reader.encoding, sys.getdefaultencoding())
reader = Reader(encoding='foo')
self.assertEquals(reader.encoding, 'foo')
def test_read(self):
"""
Test read().
"""
reader = Reader()
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)
def test_read__encoding(self):
"""
Test read(): encoding attribute respected.
"""
reader = Reader()
path = self._get_path('nonascii.mustache')
self.assertRaises(UnicodeDecodeError, reader.read, path)
reader.encoding = 'utf-8'
self.assertEquals(reader.read(path), u'non-ascii: é')
def test_get__decode_errors(self):
"""
Test get(): decode_errors attribute.
"""
reader = Reader()
path = self._get_path('nonascii.mustache')
self.assertRaises(UnicodeDecodeError, reader.read, path)
reader.decode_errors = 'replace'
self.assertEquals(reader.read(path), u'non-ascii: \ufffd\ufffd')
......@@ -35,39 +35,35 @@ class RendererInitTestCase(unittest.TestCase):
Test that the default loader is constructed correctly.
"""
r = Renderer()
actual = r.loader
renderer = Renderer()
actual = renderer.loader
expected = Loader()
self.assertEquals(type(actual), type(expected))
self.assertEquals(actual.__dict__, expected.__dict__)
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__default_encoding(self):
"""
Test that the default loader inherits the default_encoding.
Test that the default loader inherits default_encoding.
"""
r = Renderer(default_encoding='foo')
actual = r.loader
renderer = Renderer(default_encoding='foo')
reader = renderer.loader.reader
expected = Loader(encoding='foo')
self.assertEquals(actual.template_encoding, expected.template_encoding)
# Check all attributes for good measure.
self.assertEquals(actual.__dict__, expected.__dict__)
self.assertEquals(reader.encoding, 'foo')
def test_loader__default__decode_errors(self):
"""
Test that the default loader inherits the decode_errors.
Test that the default loader inherits decode_errors.
"""
r = Renderer(decode_errors='foo')
actual = r.loader
renderer = Renderer(decode_errors='foo')
reader = renderer.loader.reader
expected = Loader(decode_errors='foo')
self.assertEquals(actual.decode_errors, expected.decode_errors)
# Check all attributes for good measure.
self.assertEquals(actual.__dict__, expected.__dict__)
self.assertEquals(reader.decode_errors, 'foo')
def test_escape__default(self):
escape = Renderer().escape
......
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