From d2d854f096d9e72a6fb074d6e2209287368718ae Mon Sep 17 00:00:00 2001 From: Martin Pengelly-Phillips Date: Thu, 14 Mar 2013 12:19:38 +0000 Subject: [PATCH 1/3] Support mappings that are not simply dicts. --- pystache/context.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pystache/context.py b/pystache/context.py index 67159160..14c396f1 100644 --- a/pystache/context.py +++ b/pystache/context.py @@ -7,13 +7,15 @@ stack elements: hashes and objects. For the purposes of interpreting the spec, we define these categories mutually exclusively as follows: - (1) Hash: an item whose type is a subclass of dict. + (1) Hash: an item whose type is a subclass of collections.Mapping. (2) Object: an item that is neither a hash nor an instance of a built-in type. """ +import collections + from pystache.common import PystacheError @@ -43,7 +45,7 @@ def _get_value(context, key): The ContextStack.get() docstring documents this function's intended behavior. """ - if isinstance(context, dict): + if isinstance(context, collections.Mapping): # Then we consider the argument a "hash" for the purposes of the spec. # # We do a membership test to avoid using exceptions for flow control From c80a231df00e02038cfa7472da1af9c51d9e6173 Mon Sep 17 00:00:00 2001 From: Martin Pengelly-Phillips Date: Tue, 19 Mar 2013 14:13:01 +0000 Subject: [PATCH 2/3] Add documentation/code comment notes on reason for collections.Mapping use. --- pystache/context.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pystache/context.py b/pystache/context.py index 14c396f1..7ceeffea 100644 --- a/pystache/context.py +++ b/pystache/context.py @@ -9,6 +9,12 @@ (1) Hash: an item whose type is a subclass of collections.Mapping. + .. note:: + + collections.Mapping is used instead of a dict in order to allow other + mapping types to be valid contexts. See + `Issue 144 ` + (2) Object: an item that is neither a hash nor an instance of a built-in type. @@ -47,6 +53,9 @@ def _get_value(context, key): """ if isinstance(context, collections.Mapping): # Then we consider the argument a "hash" for the purposes of the spec. + # Using collections.Mapping allows other mapping types to be registered + # as valid "hash" contexts. See + # `Issue 144 ` # # We do a membership test to avoid using exceptions for flow control # (e.g. catching KeyError). From db9f55e1cee79f90b29e0732d7f0bad9945279aa Mon Sep 17 00:00:00 2001 From: Martin Pengelly-Phillips Date: Tue, 19 Mar 2013 16:42:39 +0000 Subject: [PATCH 3/3] Add unit test for collection.Mapping support. --- pystache/tests/test_context.py | 46 ++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/pystache/tests/test_context.py b/pystache/tests/test_context.py index 238e4b00..a7de6333 100644 --- a/pystache/tests/test_context.py +++ b/pystache/tests/test_context.py @@ -7,6 +7,7 @@ from datetime import datetime import unittest +import collections from pystache.context import _NOT_FOUND, _get_value, KeyNotFoundError, ContextStack from pystache.tests.common import AssertIsMixin, AssertStringMixin, AssertExceptionMixin, Attachable @@ -102,6 +103,51 @@ class DictSubclass(dict): pass self.assertEqual(_get_value(item, "foo"), "bar") + def test_dictionary__mapping_implementation(self): + """ + Test that registered implementations of collections.Mapping are treated as dictionaries. + + See https://github.com/defunkt/pystache/pull/144 + + """ + class MappingSubclass(collections.Mapping): + + def __init__(self, *args, **kwargs): + self._mapping = dict(*args, **kwargs) + super(MappingSubclass, self).__init__() + + def __getitem__(self, key): + return self._mapping[key] + + def __iter__(self): + return iter(self._mapping) + + def __len__(self): + return len(self._mapping) + + class RegisteredMapping(object): + + def __init__(self, *args, **kwargs): + self._mapping = dict(*args, **kwargs) + super(RegisteredMapping, self).__init__() + + def __getitem__(self, key): + return self._mapping[key] + + def __iter__(self): + return iter(self._mapping) + + def __len__(self): + return len(self._mapping) + + collections.Mapping.register(RegisteredMapping) + + item = MappingSubclass(foo="bar") + self.assertEqual(_get_value(item, "foo"), "bar") + + item = RegisteredMapping(foo="bar") + self.assertEqual(_get_value(item, "foo"), "bar") + ### Case: the item is an object. def test_object__attribute_present(self):