summaryrefslogtreecommitdiff
path: root/pystache
diff options
context:
space:
mode:
authorChris Jerdonek <chris.jerdonek@gmail.com>2012-05-04 06:31:10 -0700
committerChris Jerdonek <chris.jerdonek@gmail.com>2012-05-04 06:31:10 -0700
commit1953e28d4d7ef88c7f7a76fc66693c4c3af167bb (patch)
treeb467bc7b9da3638f7582568e007cd61bbd9196e8 /pystache
parent4a4b4ddc5411e9e938cfe9f6c8247bdce29a75a6 (diff)
downloadpystache-1953e28d4d7ef88c7f7a76fc66693c4c3af167bb.tar.gz
Finished implementing strict mode for missing tags.
Diffstat (limited to 'pystache')
-rw-r--r--pystache/renderengine.py32
-rw-r--r--pystache/renderer.py38
-rw-r--r--pystache/tests/test_renderengine.py47
-rw-r--r--pystache/tests/test_renderer.py40
4 files changed, 108 insertions, 49 deletions
diff --git a/pystache/renderengine.py b/pystache/renderengine.py
index f2974bb..6e83993 100644
--- a/pystache/renderengine.py
+++ b/pystache/renderengine.py
@@ -7,10 +7,17 @@ Defines a class responsible for rendering logic.
import re
-from pystache.context import KeyNotFoundError
from pystache.parser import Parser
+def context_get(stack, name):
+ """
+ Find and return a name from a ContextStack instance.
+
+ """
+ return stack.get(name)
+
+
class RenderEngine(object):
"""
@@ -30,14 +37,11 @@ class RenderEngine(object):
"""
- def __init__(self, resolve_partial=None, literal=None, escape=None):
+ def __init__(self, literal=None, escape=None, resolve_context=None,
+ resolve_partial=None):
"""
Arguments:
- resolve_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).
-
literal: the function used to convert unescaped variable tag
values to unicode, e.g. the value corresponding to a tag
"{{{name}}}". The function should accept a string of type
@@ -59,17 +63,21 @@ class RenderEngine(object):
incoming strings of type markupsafe.Markup differently
from plain unicode strings.
+ resolve_context: the function to call to resolve a name against
+ a context stack. The function should accept two positional
+ arguments: a ContextStack instance and a name to resolve.
+
+ resolve_partial: the function to call when loading a partial.
+ The function should accept a template name string and return a
+ template string of type unicode (not a subclass).
+
"""
self.escape = escape
self.literal = literal
+ self.resolve_context = resolve_context
self.resolve_partial = resolve_partial
- def resolve_context(self, stack, name):
- try:
- return stack.get(name)
- except KeyNotFoundError:
- return u''
-
+ # TODO: Rename context to stack throughout this module.
def _get_string_value(self, context, tag_name):
"""
Get a value from the given context as a basestring instance.
diff --git a/pystache/renderer.py b/pystache/renderer.py
index f8c40a9..7fc6fa8 100644
--- a/pystache/renderer.py
+++ b/pystache/renderer.py
@@ -9,9 +9,9 @@ import sys
from pystache import defaults
from pystache.common import TemplateNotFoundError, MissingTags
-from pystache.context import ContextStack
+from pystache.context import ContextStack, KeyNotFoundError
from pystache.loader import Loader
-from pystache.renderengine import RenderEngine
+from pystache.renderengine import context_get, RenderEngine
from pystache.specloader import SpecLoader
from pystache.template_spec import TemplateSpec
@@ -258,6 +258,13 @@ class Renderer(object):
return load_partial
+ def _is_missing_tags_strict(self):
+ """
+ Return whether missing_tags is set to strict.
+
+ """
+ return self.missing_tags == MissingTags.strict
+
def _make_resolve_partial(self):
"""
Return the resolve_partial function to pass to RenderEngine.__init__().
@@ -265,7 +272,7 @@ class Renderer(object):
"""
load_partial = self._make_load_partial()
- if self.missing_tags == MissingTags.strict:
+ if self._is_missing_tags_strict():
return load_partial
# Otherwise, ignore missing tags.
@@ -277,16 +284,35 @@ class Renderer(object):
return resolve_partial
+ def _make_resolve_context(self):
+ """
+ Return the resolve_context function to pass to RenderEngine.__init__().
+
+ """
+ if self._is_missing_tags_strict():
+ return context_get
+ # Otherwise, ignore missing tags.
+
+ def resolve_context(stack, name):
+ try:
+ return context_get(stack, name)
+ except KeyNotFoundError:
+ return u''
+
+ return resolve_context
+
def _make_render_engine(self):
"""
Return a RenderEngine instance for rendering.
"""
+ resolve_context = self._make_resolve_context()
resolve_partial = self._make_resolve_partial()
- engine = RenderEngine(resolve_partial=resolve_partial,
- literal=self._to_unicode_hard,
- escape=self._escape_to_unicode)
+ engine = RenderEngine(literal=self._to_unicode_hard,
+ escape=self._escape_to_unicode,
+ resolve_context=resolve_context,
+ resolve_partial=resolve_partial)
return engine
# TODO: add unit tests for this method.
diff --git a/pystache/tests/test_renderengine.py b/pystache/tests/test_renderengine.py
index b489168..e411ce9 100644
--- a/pystache/tests/test_renderengine.py
+++ b/pystache/tests/test_renderengine.py
@@ -7,11 +7,11 @@ Unit tests of renderengine.py.
import unittest
-from pystache.context import ContextStack
+from pystache.context import ContextStack, KeyNotFoundError
from pystache import defaults
from pystache.parser import ParsingError
-from pystache.renderengine import RenderEngine
-from pystache.tests.common import AssertStringMixin, Attachable
+from pystache.renderengine import context_get, RenderEngine
+from pystache.tests.common import AssertStringMixin, AssertExceptionMixin, Attachable
def mock_literal(s):
@@ -52,7 +52,7 @@ class RenderEngineTestCase(unittest.TestCase):
self.assertEqual(engine.resolve_partial, "foo")
-class RenderTests(unittest.TestCase, AssertStringMixin):
+class RenderTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
"""
Tests RenderEngine.render().
@@ -69,7 +69,10 @@ class RenderTests(unittest.TestCase, AssertStringMixin):
"""
escape = defaults.TAG_ESCAPE
- engine = RenderEngine(literal=unicode, escape=escape, resolve_partial=None)
+
+ engine = RenderEngine(literal=unicode, escape=escape,
+ resolve_context=context_get,
+ resolve_partial=None)
return engine
def _assert_render(self, expected, template, *context, **kwargs):
@@ -594,33 +597,15 @@ class RenderTests(unittest.TestCase, AssertStringMixin):
context = {'person': person}
self._assert_render(u'Hello, Biggles. I see you are 42.', template, context)
- def test_dot_notation__missing_attributes_or_keys(self):
- """
- Test dot notation with missing keys or attributes.
-
- Check that if a key or attribute in a dotted name does not exist, then
- the tag renders as the empty string.
-
- """
- template = """I cannot see {{person.name}}'s age: {{person.age}}.
- Nor {{other_person.name}}'s: ."""
- expected = u"""I cannot see Biggles's age: .
- Nor Mr. Bradshaw's: ."""
- context = {'person': {'name': 'Biggles'},
- 'other_person': Attachable(name='Mr. Bradshaw')}
- self._assert_render(expected, template, context)
-
def test_dot_notation__multiple_levels(self):
"""
Test dot notation with multiple levels.
"""
template = """Hello, Mr. {{person.name.lastname}}.
- I see you're back from {{person.travels.last.country.city}}.
- I'm missing some of your details: {{person.details.private.editor}}."""
+ I see you're back from {{person.travels.last.country.city}}."""
expected = u"""Hello, Mr. Pither.
- I see you're back from Cornwall.
- I'm missing some of your details: ."""
+ I see you're back from Cornwall."""
context = {'person': {'name': {'firstname': 'unknown', 'lastname': 'Pither'},
'travels': {'last': {'country': {'city': 'Cornwall'}}},
'details': {'public': 'likes cycling'}}}
@@ -652,6 +637,14 @@ class RenderTests(unittest.TestCase, AssertStringMixin):
https://github.com/mustache/spec/pull/48
"""
- template = '{{a.b}} :: ({{#c}}{{a}} :: {{a.b}}{{/c}})'
context = {'a': {'b': 'A.B'}, 'c': {'a': 'A'} }
- self._assert_render(u'A.B :: (A :: )', template, context)
+
+ template = '{{a.b}}'
+ self._assert_render(u'A.B', template, context)
+
+ template = '{{#c}}{{a}}{{/c}}'
+ self._assert_render(u'A', template, context)
+
+ template = '{{#c}}{{a.b}}{{/c}}'
+ self.assertException(KeyNotFoundError, "Key u'a.b' not found: missing u'b'",
+ self._assert_render, u'A.B :: (A :: )', template, context)
diff --git a/pystache/tests/test_renderer.py b/pystache/tests/test_renderer.py
index 9c40d2a..8bc8941 100644
--- a/pystache/tests/test_renderer.py
+++ b/pystache/tests/test_renderer.py
@@ -14,6 +14,7 @@ from examples.simple import Simple
from pystache import Renderer
from pystache import TemplateSpec
from pystache.common import TemplateNotFoundError
+from pystache.context import ContextStack, KeyNotFoundError
from pystache.loader import Loader
from pystache.tests.common import get_data_path, AssertStringMixin, AssertExceptionMixin
@@ -461,7 +462,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
self.assertEqual(actual, "abc")
self.assertEqual(type(actual), unicode)
- def test__resolve_partial__not_found__default(self):
+ def test__resolve_partial__not_found(self):
"""
Check that resolve_partial returns the empty string when a template is not found.
@@ -473,7 +474,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
self.assertString(resolve_partial('foo'), u'')
- def test__resolve_partial__not_found__strict__default(self):
+ def test__resolve_partial__not_found__missing_tags_strict(self):
"""
Check that resolve_partial provides a nice message when a template is not found.
@@ -487,7 +488,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
self.assertException(TemplateNotFoundError, "File 'foo.mustache' not found in dirs: ['.']",
resolve_partial, "foo")
- def test__resolve_partial__not_found__dict(self):
+ def test__resolve_partial__not_found__partials_dict(self):
"""
Check that resolve_partial returns the empty string when a template is not found.
@@ -500,7 +501,7 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
self.assertString(resolve_partial('foo'), u'')
- def test__resolve_partial__not_found__strict__dict(self):
+ def test__resolve_partial__not_found__partials_dict__missing_tags_strict(self):
"""
Check that resolve_partial provides a nice message when a template is not found.
@@ -638,3 +639,34 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, Asser
self.assertTrue(isinstance(s, unicode))
self.assertEqual(type(escape(s)), unicode)
+ ## Test the engine's resolve_context attribute.
+
+ def test__resolve_context(self):
+ """
+ Check resolve_context(): default arguments.
+
+ """
+ renderer = Renderer()
+
+ engine = renderer._make_render_engine()
+
+ stack = ContextStack({'foo': 'bar'})
+
+ self.assertEqual('bar', engine.resolve_context(stack, 'foo'))
+ self.assertString(u'', engine.resolve_context(stack, 'missing'))
+
+ def test__resolve_context__missing_tags_strict(self):
+ """
+ Check resolve_context(): missing_tags 'strict'.
+
+ """
+ renderer = Renderer()
+ renderer.missing_tags = 'strict'
+
+ engine = renderer._make_render_engine()
+
+ stack = ContextStack({'foo': 'bar'})
+
+ self.assertEqual('bar', engine.resolve_context(stack, 'foo'))
+ self.assertException(KeyNotFoundError, "Key 'missing' not found: first part",
+ engine.resolve_context, stack, 'missing')