Commit 77a6a232 by Chris Jerdonek

Some clean-ups to the context module (docstring changes, added TODO's, etc).

parent d94c5e1b
# coding: utf-8 # 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): ...@@ -27,6 +27,8 @@ def _is_callable(obj):
return hasattr(obj, '__call__') 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): def _get_value(item, key):
""" """
Retrieve a key's value from an item. Retrieve a key's value from an item.
...@@ -168,29 +170,38 @@ class ContextStack(object): ...@@ -168,29 +170,38 @@ class ContextStack(object):
return context return context
# TODO: add some unit tests for this. # 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 This function follows the rules outlined in the section of the spec
regarding tag interpolation. regarding tag interpolation.
Arguments: 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. This function does not coerce the return value to a string.
""" """
if name == '.': if name == '.':
# TODO: should we add a test case for an empty context stack?
return self.top() return self.top()
parts = name.split('.') 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. 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. # The full context stack is not used to resolve the remaining parts.
# From the spec-- # From the spec--
# #
...@@ -198,24 +209,23 @@ class ContextStack(object): ...@@ -198,24 +209,23 @@ class ContextStack(object):
# against a context stack containing only the result from the former # against a context stack containing only the result from the former
# resolution. # resolution.
# #
for part in parts[1:]: # TODO: make sure we have a test case for the above point.
# TODO: consider using EAFP here instead.
# http://docs.python.org/glossary.html#term-eafp
if value is _NOT_FOUND:
break
value = _get_value(value, part) 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: if value is _NOT_FOUND:
return '' return default
return value return value
# TODO: rename this method _get_part(). # TODO: combine the docstring for this method with the docstring for
def get(self, key, default=None): # 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 This method queries items in the stack in order from last-added
objects to first (last in, first out). The value returned is objects to first (last in, first out). The value returned is
...@@ -280,15 +290,16 @@ class ContextStack(object): ...@@ -280,15 +290,16 @@ class ContextStack(object):
TODO: explain the rationale for this difference in treatment. TODO: explain the rationale for this difference in treatment.
""" """
for obj in reversed(self._stack): val = _NOT_FOUND
val = _get_value(obj, key)
for item in reversed(self._stack):
val = _get_value(item, key)
if val is _NOT_FOUND: if val is _NOT_FOUND:
continue continue
# Otherwise, the key was found. # Otherwise, the key was found.
return val break
# Otherwise, no item in the stack contained the key.
return default return val
def push(self, item): def push(self, item):
""" """
......
...@@ -68,7 +68,7 @@ class RenderEngine(object): ...@@ -68,7 +68,7 @@ class RenderEngine(object):
Get a value from the given context as a basestring instance. Get a value from the given context as a basestring instance.
""" """
val = context.resolve(tag_name) val = context.get(tag_name)
if callable(val): if callable(val):
# According to the spec: # According to the spec:
...@@ -135,7 +135,7 @@ class RenderEngine(object): ...@@ -135,7 +135,7 @@ class RenderEngine(object):
""" """
# TODO: is there a bug because we are not using the same # TODO: is there a bug because we are not using the same
# logic as in _get_string_value()? # logic as in _get_string_value()?
data = context.resolve(name) data = context.get(name)
# Per the spec, lambdas in inverted sections are considered truthy. # Per the spec, lambdas in inverted sections are considered truthy.
if data: if data:
return u'' return u''
...@@ -154,7 +154,7 @@ class RenderEngine(object): ...@@ -154,7 +154,7 @@ class RenderEngine(object):
""" """
template = template_ template = template_
parsed_template = parsed_template_ parsed_template = parsed_template_
data = context.resolve(name) data = context.get(name)
# From the spec: # From the spec:
# #
......
...@@ -11,7 +11,7 @@ import unittest ...@@ -11,7 +11,7 @@ import unittest
from pystache.context import _NOT_FOUND from pystache.context import _NOT_FOUND
from pystache.context import _get_value from pystache.context import _get_value
from pystache.context import ContextStack from pystache.context import ContextStack
from pystache.tests.common import AssertIsMixin from pystache.tests.common import AssertIsMixin, AssertStringMixin
class SimpleObject(object): class SimpleObject(object):
...@@ -204,7 +204,7 @@ class GetValueTests(unittest.TestCase, AssertIsMixin): ...@@ -204,7 +204,7 @@ class GetValueTests(unittest.TestCase, AssertIsMixin):
self.assertNotFound(item2, 'pop') self.assertNotFound(item2, 'pop')
class ContextStackTests(unittest.TestCase, AssertIsMixin): class ContextStackTests(unittest.TestCase, AssertIsMixin, AssertStringMixin):
""" """
Test the ContextStack class. Test the ContextStack class.
...@@ -320,7 +320,7 @@ class ContextStackTests(unittest.TestCase, AssertIsMixin): ...@@ -320,7 +320,7 @@ class ContextStackTests(unittest.TestCase, AssertIsMixin):
""" """
context = ContextStack() context = ContextStack()
self.assertTrue(context.get("foo") is None) self.assertString(context.get("foo"), u'')
def test_get__default(self): def test_get__default(self):
""" """
......
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