diff options
Diffstat (limited to 'pystache')
-rw-r--r-- | pystache/renderengine.py | 32 | ||||
-rw-r--r-- | pystache/renderer.py | 38 | ||||
-rw-r--r-- | pystache/tests/test_renderengine.py | 47 | ||||
-rw-r--r-- | pystache/tests/test_renderer.py | 40 |
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') |