Commit ad2686cf by Chris Jerdonek

Merge branch 'issue-110-not-found-tags' into 'development':

Finished addressing issue #110: "Missing tag values always treated as empty"
parents 170c65df 93a2a84e
......@@ -4,6 +4,8 @@ History
0.5.3 (TBD)
-----------
* Added option of raising errors on missing tags/partials:
``Renderer(missing_tags='strict')`` (issue #110).
* Bugfix: exceptions raised from a property are no longer swallowed when
getting a key from a context stack (issue #110).
......
......@@ -26,6 +26,14 @@ def read(path):
f.close()
class MissingTags(object):
"""Contains the valid values for Renderer.missing_tags."""
ignore = 'ignore'
strict = 'strict'
class PystacheError(Exception):
"""Base class for Pystache exceptions."""
pass
......
......@@ -14,6 +14,9 @@ spec, we define these categories mutually exclusively as follows:
"""
from pystache.common import PystacheError
# This equals '__builtin__' in Python 2 and 'builtins' in Python 3.
_BUILTIN_MODULE = type(0).__module__
......@@ -73,6 +76,21 @@ def _get_value(context, key):
return _NOT_FOUND
class KeyNotFoundError(PystacheError):
"""
An exception raised when a key is not found in a context stack.
"""
def __init__(self, key, details):
self.key = key
self.details = details
def __str__(self):
return "Key %s not found: %s" % (repr(self.key), self.details)
class ContextStack(object):
"""
......@@ -182,7 +200,7 @@ class ContextStack(object):
# TODO: add more unit tests for this.
# TODO: update the docstring for dotted names.
def get(self, name, default=u''):
def get(self, name):
"""
Resolve a dotted name against the current context stack.
......@@ -252,18 +270,19 @@ class ContextStack(object):
"""
if name == '.':
# TODO: should we add a test case for an empty context stack?
return self.top()
try:
return self.top()
except IndexError:
raise KeyNotFoundError(".", "empty context stack")
parts = name.split('.')
result = self._get_simple(parts[0])
try:
result = self._get_simple(parts[0])
except KeyNotFoundError:
raise KeyNotFoundError(name, "first part")
for part in parts[1:]:
# TODO: consider using EAFP here instead.
# http://docs.python.org/glossary.html#term-eafp
if result is _NOT_FOUND:
break
# The full context stack is not used to resolve the remaining parts.
# From the spec--
#
......@@ -275,9 +294,10 @@ class ContextStack(object):
#
# TODO: make sure we have a test case for the above point.
result = _get_value(result, part)
if result is _NOT_FOUND:
return default
# TODO: consider using EAFP here instead.
# http://docs.python.org/glossary.html#term-eafp
if result is _NOT_FOUND:
raise KeyNotFoundError(name, "missing %s" % repr(part))
return result
......@@ -286,16 +306,12 @@ class ContextStack(object):
Query the stack for a non-dotted name.
"""
result = _NOT_FOUND
for item in reversed(self._stack):
result = _get_value(item, name)
if result is _NOT_FOUND:
continue
# Otherwise, the key was found.
break
if result is not _NOT_FOUND:
return result
return result
raise KeyNotFoundError(name, "part missing")
def push(self, item):
"""
......
......@@ -17,6 +17,8 @@ except ImportError:
import os
import sys
from pystache.common import MissingTags
# How to handle encoding errors when decoding strings from str to unicode.
#
......@@ -36,6 +38,9 @@ STRING_ENCODING = sys.getdefaultencoding()
# strings that arise from files.
FILE_ENCODING = sys.getdefaultencoding()
# How to handle missing tags when rendering a template.
MISSING_TAGS = MissingTags.ignore
# The starting list of directories in which to search for templates when
# loading a template by file name.
SEARCH_DIRS = [os.curdir] # i.e. ['.']
......
......@@ -9,7 +9,6 @@ This module is only meant for internal use by the renderengine module.
import re
from pystache.common import TemplateNotFoundError
from pystache.parsed import ParsedTemplate
......@@ -216,15 +215,9 @@ 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''
template = engine.resolve_partial(tag_key)
# Indent before rendering.
template = re.sub(NON_BLANK_RE, leading_whitespace + ur'\1', template)
func = engine._make_get_partial(template)
else:
......
......@@ -10,6 +10,14 @@ import re
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):
"""
......@@ -29,15 +37,11 @@ class RenderEngine(object):
"""
def __init__(self, load_partial=None, literal=None, escape=None):
def __init__(self, literal=None, escape=None, resolve_context=None,
resolve_partial=None):
"""
Arguments:
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). 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
"{{{name}}}". The function should accept a string of type
......@@ -59,18 +63,27 @@ 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.load_partial = load_partial
self.resolve_context = resolve_context
self.resolve_partial = resolve_partial
# TODO: rename context to stack throughout this module.
# 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.
"""
val = context.get(tag_name)
val = self.resolve_context(context, tag_name)
if callable(val):
# According to the spec:
......@@ -138,7 +151,7 @@ class RenderEngine(object):
"""
# TODO: is there a bug because we are not using the same
# logic as in _get_string_value()?
data = context.get(name)
data = self.resolve_context(context, name)
# Per the spec, lambdas in inverted sections are considered truthy.
if data:
return u''
......@@ -157,7 +170,7 @@ class RenderEngine(object):
"""
template = template_
parsed_template = parsed_template_
data = context.get(name)
data = self.resolve_context(context, name)
# From the spec:
#
......
......@@ -8,10 +8,10 @@ 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.common import TemplateNotFoundError, MissingTags
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
......@@ -27,6 +27,7 @@ else:
_STRING_TYPES = (unicode, type(u"a".encode('utf-8')))
class Renderer(object):
"""
......@@ -49,7 +50,7 @@ class Renderer(object):
def __init__(self, file_encoding=None, string_encoding=None,
decode_errors=None, search_dirs=None, file_extension=None,
escape=None, partials=None):
escape=None, partials=None, missing_tags=None):
"""
Construct an instance.
......@@ -104,6 +105,11 @@ class Renderer(object):
argument to the built-in function unicode(). Defaults to the
package default.
missing_tags: a string specifying how to handle missing tags.
If 'strict', an error is raised on a missing tag. If 'ignore',
the value of the tag is the empty string. Defaults to the
package default.
"""
if decode_errors is None:
decode_errors = defaults.DECODE_ERRORS
......@@ -117,6 +123,9 @@ class Renderer(object):
if file_extension is None:
file_extension = defaults.TEMPLATE_EXTENSION
if missing_tags is None:
missing_tags = defaults.MISSING_TAGS
if search_dirs is None:
search_dirs = defaults.SEARCH_DIRS
......@@ -131,6 +140,7 @@ class Renderer(object):
self.escape = escape
self.file_encoding = file_encoding
self.file_extension = file_extension
self.missing_tags = missing_tags
self.partials = partials
self.search_dirs = search_dirs
self.string_encoding = string_encoding
......@@ -224,21 +234,21 @@ class Renderer(object):
def _make_load_partial(self):
"""
Return the load_partial function to pass to RenderEngine.__init__().
Return a function that loads a partial by name.
"""
if self.partials is None:
load_template = self._make_load_template()
return load_template
return self._make_load_template()
# Otherwise, create a load_partial function from the custom partial
# loader that satisfies RenderEngine requirements (and that provides
# a nicer exception, etc).
# Otherwise, create a function from the custom partial loader.
partials = self.partials
def load_partial(name):
# TODO: consider using EAFP here instead.
# http://docs.python.org/glossary.html#term-eafp
# This would mean requiring that the custom partial loader
# raise a KeyError on name not found.
template = partials.get(name)
if template is None:
raise TemplateNotFoundError("Name %s not found in partials: %s" %
(repr(name), type(partials)))
......@@ -248,16 +258,61 @@ 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__().
"""
load_partial = self._make_load_partial()
if self._is_missing_tags_strict():
return load_partial
# Otherwise, ignore missing tags.
def resolve_partial(name):
try:
return load_partial(name)
except TemplateNotFoundError:
return u''
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.
"""
load_partial = self._make_load_partial()
resolve_context = self._make_resolve_context()
resolve_partial = self._make_resolve_partial()
engine = RenderEngine(load_partial=load_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.
......
......@@ -8,10 +8,8 @@ Unit tests of context.py.
from datetime import datetime
import unittest
from pystache.context import _NOT_FOUND
from pystache.context import _get_value
from pystache.context import ContextStack
from pystache.tests.common import AssertIsMixin, AssertStringMixin, Attachable
from pystache.context import _NOT_FOUND, _get_value, KeyNotFoundError, ContextStack
from pystache.tests.common import AssertIsMixin, AssertStringMixin, AssertExceptionMixin, Attachable
class SimpleObject(object):
......@@ -39,7 +37,7 @@ class DictLike(object):
return self._dict[key]
class GetValueTests(unittest.TestCase, AssertIsMixin):
class GetValueTestCase(unittest.TestCase, AssertIsMixin):
"""Test context._get_value()."""
......@@ -224,7 +222,8 @@ class GetValueTests(unittest.TestCase, AssertIsMixin):
self.assertNotFound(item2, 'pop')
class ContextStackTests(unittest.TestCase, AssertIsMixin, AssertStringMixin):
class ContextStackTestCase(unittest.TestCase, AssertIsMixin, AssertStringMixin,
AssertExceptionMixin):
"""
Test the ContextStack class.
......@@ -326,6 +325,24 @@ class ContextStackTests(unittest.TestCase, AssertIsMixin, AssertStringMixin):
context = ContextStack.create({'foo': 'bar'}, foo='buzz')
self.assertEqual(context.get('foo'), 'buzz')
## Test the get() method.
def test_get__single_dot(self):
"""
Test getting a single dot (".").
"""
context = ContextStack("a", "b")
self.assertEqual(context.get("."), "b")
def test_get__single_dot__missing(self):
"""
Test getting a single dot (".") with an empty context stack.
"""
context = ContextStack()
self.assertException(KeyNotFoundError, "Key '.' not found: empty context stack", context.get, ".")
def test_get__key_present(self):
"""
Test getting a key.
......@@ -340,15 +357,7 @@ class ContextStackTests(unittest.TestCase, AssertIsMixin, AssertStringMixin):
"""
context = ContextStack()
self.assertString(context.get("foo"), u'')
def test_get__default(self):
"""
Test that get() respects the default value.
"""
context = ContextStack()
self.assertEqual(context.get("foo", "bar"), "bar")
self.assertException(KeyNotFoundError, "Key 'foo' not found: first part", context.get, "foo")
def test_get__precedence(self):
"""
......@@ -444,10 +453,10 @@ class ContextStackTests(unittest.TestCase, AssertIsMixin, AssertStringMixin):
def test_dot_notation__missing_attr_or_key(self):
name = "foo.bar.baz.bak"
stack = ContextStack({"foo": {"bar": {}}})
self.assertString(stack.get(name), u'')
self.assertException(KeyNotFoundError, "Key 'foo.bar.baz.bak' not found: missing 'baz'", stack.get, name)
stack = ContextStack({"foo": Attachable(bar=Attachable())})
self.assertString(stack.get(name), u'')
self.assertException(KeyNotFoundError, "Key 'foo.bar.baz.bak' not found: missing 'baz'", stack.get, name)
def test_dot_notation__missing_part_terminates_search(self):
"""
......@@ -471,7 +480,7 @@ class ContextStackTests(unittest.TestCase, AssertIsMixin, AssertStringMixin):
"""
stack = ContextStack({'a': {'b': 'A.B'}}, {'a': 'A'})
self.assertEqual(stack.get('a'), 'A')
self.assertString(stack.get('a.b'), u'')
self.assertException(KeyNotFoundError, "Key 'a.b' not found: missing 'b'", stack.get, "a.b")
stack.pop()
self.assertEqual(stack.get('a.b'), 'A.B')
......
......@@ -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):
......@@ -45,14 +45,14 @@ class RenderEngineTestCase(unittest.TestCase):
"""
# In real-life, these arguments would be functions
engine = RenderEngine(load_partial="foo", literal="literal", escape="escape")
engine = RenderEngine(resolve_partial="foo", literal="literal", escape="escape")
self.assertEqual(engine.escape, "escape")
self.assertEqual(engine.literal, "literal")
self.assertEqual(engine.load_partial, "foo")
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, load_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):
......@@ -81,7 +84,7 @@ class RenderTests(unittest.TestCase, AssertStringMixin):
engine = kwargs.get('engine', self._engine())
if partials is not None:
engine.load_partial = lambda key: unicode(partials[key])
engine.resolve_partial = lambda key: unicode(partials[key])
context = ContextStack(*context)
......@@ -92,14 +95,14 @@ class RenderTests(unittest.TestCase, AssertStringMixin):
def test_render(self):
self._assert_render(u'Hi Mom', 'Hi {{person}}', {'person': 'Mom'})
def test__load_partial(self):
def test__resolve_partial(self):
"""
Test that render() uses the load_template attribute.
"""
engine = self._engine()
partials = {'partial': u"{{person}}"}
engine.load_partial = lambda key: partials[key]
engine.resolve_partial = lambda key: partials[key]
self._assert_render(u'Hi Mom', 'Hi {{>partial}}', {'person': 'Mom'}, engine=engine)
......@@ -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)
......@@ -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
......@@ -124,6 +125,22 @@ class RendererInitTestCase(unittest.TestCase):
renderer = Renderer(file_extension='foo')
self.assertEqual(renderer.file_extension, 'foo')
def test_missing_tags(self):
"""
Check that the missing_tags attribute is set correctly.
"""
renderer = Renderer(missing_tags='foo')
self.assertEqual(renderer.missing_tags, 'foo')
def test_missing_tags__default(self):
"""
Check the missing_tags default.
"""
renderer = Renderer()
self.assertEqual(renderer.missing_tags, 'ignore')
def test_search_dirs__default(self):
"""
Check the search_dirs default.
......@@ -319,37 +336,37 @@ class RendererTests(unittest.TestCase, AssertStringMixin):
renderer.string_encoding = 'utf_8'
self.assertEqual(renderer.render(template), u"déf")
def test_make_load_partial(self):
def test_make_resolve_partial(self):
"""
Test the _make_load_partial() method.
Test the _make_resolve_partial() method.
"""
renderer = Renderer()
renderer.partials = {'foo': 'bar'}
load_partial = renderer._make_load_partial()
resolve_partial = renderer._make_resolve_partial()
actual = load_partial('foo')
actual = resolve_partial('foo')
self.assertEqual(actual, 'bar')
self.assertEqual(type(actual), unicode, "RenderEngine requires that "
"load_partial return unicode strings.")
"resolve_partial return unicode strings.")
def test_make_load_partial__unicode(self):
def test_make_resolve_partial__unicode(self):
"""
Test _make_load_partial(): that load_partial doesn't "double-decode" Unicode.
Test _make_resolve_partial(): that resolve_partial doesn't "double-decode" Unicode.
"""
renderer = Renderer()
renderer.partials = {'partial': 'foo'}
load_partial = renderer._make_load_partial()
self.assertEqual(load_partial("partial"), "foo")
resolve_partial = renderer._make_resolve_partial()
self.assertEqual(resolve_partial("partial"), "foo")
# Now with a value that is already unicode.
renderer.partials = {'partial': u'foo'}
load_partial = renderer._make_load_partial()
resolve_partial = renderer._make_resolve_partial()
# If the next line failed, we would get the following error:
# TypeError: decoding Unicode is not supported
self.assertEqual(load_partial("partial"), "foo")
self.assertEqual(resolve_partial("partial"), "foo")
def test_render_path(self):
"""
......@@ -406,7 +423,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, AssertExceptionMixin):
class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertStringMixin, AssertExceptionMixin):
"""
Check the RenderEngine returned by Renderer._make_render_engine().
......@@ -420,11 +437,11 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertExceptionMixin):
"""
return _make_renderer()
## Test the engine's load_partial attribute.
## Test the engine's resolve_partial attribute.
def test__load_partial__returns_unicode(self):
def test__resolve_partial__returns_unicode(self):
"""
Check that load_partial returns unicode (and not a subclass).
Check that resolve_partial returns unicode (and not a subclass).
"""
class MyUnicode(unicode):
......@@ -436,43 +453,70 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertExceptionMixin):
engine = renderer._make_render_engine()
actual = engine.load_partial('str')
actual = engine.resolve_partial('str')
self.assertEqual(actual, "foo")
self.assertEqual(type(actual), unicode)
# Check that unicode subclasses are not preserved.
actual = engine.load_partial('subclass')
actual = engine.resolve_partial('subclass')
self.assertEqual(actual, "abc")
self.assertEqual(type(actual), unicode)
def test__load_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.
"""
Check that load_partial provides a nice message when a template is not found.
renderer = Renderer()
engine = renderer._make_render_engine()
resolve_partial = engine.resolve_partial
self.assertString(resolve_partial('foo'), u'')
def test__resolve_partial__not_found__missing_tags_strict(self):
"""
Check that resolve_partial provides a nice message when a template is not found.
"""
renderer = Renderer()
renderer.missing_tags = 'strict'
engine = renderer._make_render_engine()
load_partial = engine.load_partial
resolve_partial = engine.resolve_partial
self.assertException(TemplateNotFoundError, "File 'foo.mustache' not found in dirs: ['.']",
load_partial, "foo")
resolve_partial, "foo")
def test__resolve_partial__not_found__partials_dict(self):
"""
Check that resolve_partial returns the empty string when a template is not found.
"""
renderer = Renderer()
renderer.partials = {}
engine = renderer._make_render_engine()
resolve_partial = engine.resolve_partial
def test__load_partial__not_found__dict(self):
self.assertString(resolve_partial('foo'), u'')
def test__resolve_partial__not_found__partials_dict__missing_tags_strict(self):
"""
Check that load_partial provides a nice message when a template is not found.
Check that resolve_partial provides a nice message when a template is not found.
"""
renderer = Renderer()
renderer.missing_tags = 'strict'
renderer.partials = {}
engine = renderer._make_render_engine()
load_partial = engine.load_partial
resolve_partial = engine.resolve_partial
# Include dict directly since str(dict) is different in Python 2 and 3:
# <type 'dict'> versus <class 'dict'>, respectively.
# 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")
resolve_partial, "foo")
## Test the engine's literal attribute.
......@@ -595,3 +639,34 @@ class Renderer_MakeRenderEngineTests(unittest.TestCase, AssertExceptionMixin):
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')
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