Commit 5a94c93b by Chris Jerdonek

Addressed issue #115: "Match spec expectation for partials not found"

parent 27f2ae76
......@@ -5,6 +5,7 @@ History
-----------
* Added support for dot notation and version 1.1.2 of the spec (issue #99). [rbp]
* Missing partials now render as empty string per latest version of spec (issue #115).
* Bugfix: falsey values now coerced to strings using str().
* Bugfix: lambda return values for sections no longer pushed onto context stack (issue #113).
* Bugfix: lists of lambdas for sections were not rendered (issue #114).
......
Subproject commit bf6288ed6bd0ce8ccea6f1dac070b3d779132c3b
Subproject commit 9b1bc7ad19247e9671304af02078f2ce30132665
......@@ -35,6 +35,7 @@ import sys
#
# ValueError: Attempted relative import in non-package
#
from pystache.common import TemplateNotFoundError
from pystache.renderer import Renderer
......@@ -78,7 +79,7 @@ def main(sys_argv=sys.argv):
try:
template = renderer.load_template(template)
except IOError:
except TemplateNotFoundError:
pass
try:
......
# coding: utf-8
"""
Exposes common functions.
Exposes functionality needed throughout the project.
"""
......@@ -24,3 +24,13 @@ def read(path):
return f.read()
finally:
f.close()
class PystacheError(Exception):
"""Base class for Pystache exceptions."""
pass
class TemplateNotFoundError(PystacheError):
"""An exception raised when a template is not found."""
pass
......@@ -9,6 +9,7 @@ import os
import re
import sys
from pystache.common import TemplateNotFoundError
from pystache import defaults
......@@ -117,8 +118,7 @@ class Locator(object):
path = self._find_path(search_dirs, file_name)
if path is None:
# TODO: we should probably raise an exception of our own type.
raise IOError('Template file %s not found in directories: %s' %
raise TemplateNotFoundError('File %s not found in dirs: %s' %
(repr(file_name), repr(search_dirs)))
return path
......
......@@ -32,6 +32,9 @@ class ParsedTemplate(object):
"""
self._parse_tree = parse_tree
def __repr__(self):
return "[%s]" % (", ".join([repr(part) for part in self._parse_tree]))
def render(self, context):
"""
Returns: a string of type unicode.
......
......@@ -9,12 +9,13 @@ This module is only meant for internal use by the renderengine module.
import re
from pystache.common import TemplateNotFoundError
from pystache.parsed import ParsedTemplate
DEFAULT_DELIMITERS = ('{{', '}}')
END_OF_LINE_CHARACTERS = ['\r', '\n']
NON_BLANK_RE = re.compile(r'^(.)', re.M)
DEFAULT_DELIMITERS = (u'{{', u'}}')
END_OF_LINE_CHARACTERS = [u'\r', u'\n']
NON_BLANK_RE = re.compile(ur'^(.)', re.M)
def _compile_template_re(delimiters=None):
......@@ -215,10 +216,14 @@ class Parser(object):
elif tag_type == '>':
try:
# TODO: make engine.load() and test it separately.
template = engine.load_partial(tag_key)
except TemplateNotFoundError:
template = u''
# Indent before rendering.
template = re.sub(NON_BLANK_RE, leading_whitespace + r'\1', template)
template = re.sub(NON_BLANK_RE, leading_whitespace + ur'\1', template)
func = engine._make_get_partial(template)
......
......@@ -35,7 +35,8 @@ class RenderEngine(object):
load_partial: the function to call when loading a partial. The
function should accept a string template name and return a
template string of type unicode (not a subclass).
template string of type unicode (not a subclass). If the
template is not found, it should raise a TemplateNotFoundError.
literal: the function used to convert unescaped variable tag
values to unicode, e.g. the value corresponding to a tag
......
......@@ -8,6 +8,7 @@ This module provides a Renderer class to render templates.
import sys
from pystache import defaults
from pystache.common import TemplateNotFoundError
from pystache.context import ContextStack
from pystache.loader import Loader
from pystache.renderengine import RenderEngine
......@@ -239,9 +240,8 @@ class Renderer(object):
template = partials.get(name)
if template is None:
# TODO: make a TemplateNotFoundException type that provides
# the original partials as an attribute.
raise Exception("Partial not found with name: %s" % repr(name))
raise TemplateNotFoundError("Name %s not found in partials: %s" %
(repr(name), type(partials)))
# RenderEngine requires that the return value be unicode.
return self._to_unicode_hard(template)
......
......@@ -168,6 +168,20 @@ class AssertIsMixin:
self.assertTrue(first is second, msg="%s is not %s" % (repr(first), repr(second)))
class AssertExceptionMixin:
"""A unittest.TestCase mixin adding assertException()."""
# unittest.assertRaisesRegexp() is not available until Python 2.7:
# http://docs.python.org/library/unittest.html#unittest.TestCase.assertRaisesRegexp
def assertException(self, exception_type, msg, callable, *args, **kwds):
try:
callable(*args, **kwds)
raise Exception("Expected exception: %s: %s" % (exception_type, repr(msg)))
except exception_type, err:
self.assertEqual(str(err), msg)
class SetupDefaults(object):
"""
......
......@@ -11,14 +11,15 @@ import sys
import unittest
# TODO: remove this alias.
from pystache.common import TemplateNotFoundError
from pystache.loader import Loader as Reader
from pystache.locator import Locator
from pystache.tests.common import DATA_DIR, EXAMPLES_DIR
from pystache.tests.common import DATA_DIR, EXAMPLES_DIR, AssertExceptionMixin
from pystache.tests.data.views import SayHello
class LocatorTests(unittest.TestCase):
class LocatorTests(unittest.TestCase, AssertExceptionMixin):
def _locator(self):
return Locator(search_dirs=DATA_DIR)
......@@ -110,7 +111,8 @@ class LocatorTests(unittest.TestCase):
def test_find_name__non_existent_template_fails(self):
locator = Locator()
self.assertRaises(IOError, locator.find_name, search_dirs=[], template_name='doesnt_exist')
self.assertException(TemplateNotFoundError, "File 'doesnt_exist.mustache' not found in dirs: []",
locator.find_name, search_dirs=[], template_name='doesnt_exist')
def test_find_object(self):
locator = Locator()
......
......@@ -13,9 +13,10 @@ import unittest
from examples.simple import Simple
from pystache import Renderer
from pystache import TemplateSpec
from pystache.common import TemplateNotFoundError
from pystache.loader import Loader
from pystache.tests.common import get_data_path, AssertStringMixin
from pystache.tests.common import get_data_path, AssertStringMixin, AssertExceptionMixin
from pystache.tests.data.views import SayHello
......@@ -405,7 +406,7 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
# we no longer need to exercise all rendering code paths through
# the Renderer. It suffices to test rendering paths through the
# RenderEngine for the same amount of code coverage.
class Renderer_MakeRenderEngineTests(unittest.TestCase):
class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertExceptionMixin):
"""
Check the RenderEngine returned by Renderer._make_render_engine().
......@@ -444,7 +445,20 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase):
self.assertEqual(actual, "abc")
self.assertEqual(type(actual), unicode)
def test__load_partial__not_found(self):
def test__load_partial__not_found__default(self):
"""
Check that load_partial provides a nice message when a template is not found.
"""
renderer = Renderer()
engine = renderer._make_render_engine()
load_partial = engine.load_partial
self.assertException(TemplateNotFoundError, "File 'foo.mustache' not found in dirs: ['.']",
load_partial, "foo")
def test__load_partial__not_found__dict(self):
"""
Check that load_partial provides a nice message when a template is not found.
......@@ -455,11 +469,10 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase):
engine = renderer._make_render_engine()
load_partial = engine.load_partial
try:
load_partial("foo")
raise Exception("Shouldn't get here")
except Exception, err:
self.assertEqual(str(err), "Partial not found with name: 'foo'")
# Include dict directly since str(dict) is different in Python 2 and 3:
# <type 'dict'> versus <class 'dict'>, respectively.
self.assertException(TemplateNotFoundError, "Name 'foo' not found in partials: %s" % dict,
load_partial, "foo")
## Test the engine's literal attribute.
......
......@@ -16,6 +16,7 @@ from examples.lambdas import Lambdas
from examples.inverted import Inverted, InvertedLists
from pystache import Renderer
from pystache import TemplateSpec
from pystache.common import TemplateNotFoundError
from pystache.locator import Locator
from pystache.loader import Loader
from pystache.specloader import SpecLoader
......@@ -42,7 +43,7 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
view = Tagless()
renderer = Renderer()
self.assertRaises(IOError, renderer.render, view)
self.assertRaises(TemplateNotFoundError, renderer.render, view)
# TODO: change this test to remove the following brittle line.
view.template_rel_directory = "examples"
......@@ -60,7 +61,8 @@ class ViewTestCase(unittest.TestCase, AssertStringMixin):
renderer1 = Renderer()
renderer2 = Renderer(search_dirs=EXAMPLES_DIR)
self.assertRaises(IOError, renderer1.render, spec)
actual = renderer1.render(spec)
self.assertString(actual, u"Partial: ")
actual = renderer2.render(spec)
self.assertEqual(actual, "Partial: No tags...")
......
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