diff options
-rw-r--r-- | pystache/context.py | 61 | ||||
-rw-r--r-- | pystache/renderengine.py | 6 | ||||
-rw-r--r-- | pystache/tests/test_context.py | 6 |
3 files changed, 42 insertions, 31 deletions
diff --git a/pystache/context.py b/pystache/context.py index bd927e0..cf36c7d 100644 --- a/pystache/context.py +++ b/pystache/context.py @@ -1,7 +1,7 @@ # coding: utf-8 """ -Exposes a ContextStack class and functions to retrieve names from context. +Exposes a ContextStack class. """ @@ -27,6 +27,8 @@ def _is_callable(obj): return hasattr(obj, '__call__') +# TODO: rename item to context (now that we have a separate notion of context stack). +# TODO: document what a "context" is as opposed to a context stack. def _get_value(item, key): """ Retrieve a key's value from an item. @@ -168,54 +170,62 @@ class ContextStack(object): return context # TODO: add some unit tests for this. - def resolve(self, name): + def get(self, name, default=u''): """ - Resolve a name against a context stack. + Resolve a dotted name against the current context stack. This function follows the rules outlined in the section of the spec regarding tag interpolation. Arguments: - context_stack: a ContextStack instance. + name: a dotted or non-dotted name. + + default: the value to return if name resolution fails at any point. + Defaults to the empty string since the Mustache spec says that if + name resolution fails at any point, the result should be considered + falsey, and should interpolate as the empty string. This function does not coerce the return value to a string. """ if name == '.': + # TODO: should we add a test case for an empty context stack? return self.top() parts = name.split('.') - value = self.get(parts[0], _NOT_FOUND) + value = self._get_simple(parts[0]) - # TODO: make sure we have a test case for the following point. - # - # The full context stack is not used to resolve the remaining parts. - # From the spec-- - # - # If any name parts were retained in step 1, each should be resolved - # against a context stack containing only the result from the former - # resolution. - # for part in parts[1:]: # TODO: consider using EAFP here instead. # http://docs.python.org/glossary.html#term-eafp if value is _NOT_FOUND: break + # The full context stack is not used to resolve the remaining parts. + # From the spec-- + # + # If any name parts were retained in step 1, each should be resolved + # against a context stack containing only the result from the former + # resolution. + # + # TODO: make sure we have a test case for the above point. value = _get_value(value, part) - # The spec says that if name resolution fails at any point, the result - # should be considered falsey, and should interpolate as the empty string. if value is _NOT_FOUND: - return '' + return default return value - # TODO: rename this method _get_part(). - def get(self, key, default=None): + # TODO: combine the docstring for this method with the docstring for + # the get() method. + def _get_simple(self, key): """ - Query the stack for the given key, and return the resulting value. + Query the stack for a non-dotted key, and return the resulting value. + + Arguments: + + key: a non-dotted name. This method queries items in the stack in order from last-added objects to first (last in, first out). The value returned is @@ -280,15 +290,16 @@ class ContextStack(object): TODO: explain the rationale for this difference in treatment. """ - for obj in reversed(self._stack): - val = _get_value(obj, key) + val = _NOT_FOUND + + for item in reversed(self._stack): + val = _get_value(item, key) if val is _NOT_FOUND: continue # Otherwise, the key was found. - return val - # Otherwise, no item in the stack contained the key. + break - return default + return val def push(self, item): """ diff --git a/pystache/renderengine.py b/pystache/renderengine.py index 918acfb..b05e022 100644 --- a/pystache/renderengine.py +++ b/pystache/renderengine.py @@ -68,7 +68,7 @@ class RenderEngine(object): Get a value from the given context as a basestring instance. """ - val = context.resolve(tag_name) + val = context.get(tag_name) if callable(val): # According to the spec: @@ -135,7 +135,7 @@ class RenderEngine(object): """ # TODO: is there a bug because we are not using the same # logic as in _get_string_value()? - data = context.resolve(name) + data = context.get(name) # Per the spec, lambdas in inverted sections are considered truthy. if data: return u'' @@ -154,7 +154,7 @@ class RenderEngine(object): """ template = template_ parsed_template = parsed_template_ - data = context.resolve(name) + data = context.get(name) # From the spec: # diff --git a/pystache/tests/test_context.py b/pystache/tests/test_context.py index dd9fdae..538c74e 100644 --- a/pystache/tests/test_context.py +++ b/pystache/tests/test_context.py @@ -11,7 +11,7 @@ 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 +from pystache.tests.common import AssertIsMixin, AssertStringMixin class SimpleObject(object): @@ -204,7 +204,7 @@ class GetValueTests(unittest.TestCase, AssertIsMixin): self.assertNotFound(item2, 'pop') -class ContextStackTests(unittest.TestCase, AssertIsMixin): +class ContextStackTests(unittest.TestCase, AssertIsMixin, AssertStringMixin): """ Test the ContextStack class. @@ -320,7 +320,7 @@ class ContextStackTests(unittest.TestCase, AssertIsMixin): """ context = ContextStack() - self.assertTrue(context.get("foo") is None) + self.assertString(context.get("foo"), u'') def test_get__default(self): """ |