diff --git a/chevron/renderer.py b/chevron/renderer.py index 65a00f6..796dfb7 100644 --- a/chevron/renderer.py +++ b/chevron/renderer.py @@ -48,6 +48,15 @@ def _html_escape(string): return string +def _is_instance_of_namedtuple(obj): + """Tests if an object is a namedtuple""" + return ( + isinstance(obj, tuple) and + hasattr(obj, '_asdict') and + hasattr(obj, '_fields') + ) + + def _get_key(key, scopes, warn, keep, def_ldel, def_rdel): """Get a key from the current scope""" @@ -61,16 +70,16 @@ def _get_key(key, scopes, warn, keep, def_ldel, def_rdel): try: # For every dot seperated key for child in key.split('.'): - # Move into the scope - try: - # Try subscripting (Normal dictionaries) + + # use different methods of access depending on the data structure + # passed: + if isinstance(scope, dict): scope = scope[child] - except (TypeError, AttributeError): - try: - scope = getattr(scope, child) - except (TypeError, AttributeError): - # Try as a list - scope = scope[int(child)] + elif hasattr(scope, "__dict__") or _is_instance_of_namedtuple(scope): + scope = getattr(scope, child) + else: + # Otherwise, try accessing as a list/tuple + scope = scope[int(child)] # Return an empty string if falsy, with two exceptions # 0 should return 0, and False should return False diff --git a/test_spec.py b/test_spec.py index 905e105..da0c724 100755 --- a/test_spec.py +++ b/test_spec.py @@ -466,6 +466,32 @@ def test_namedtuple_data(self): self.assertEqual(result, expected) + # Regression test for + # https://github.com/noahmorrison/chevron/issues/104 + def test_string_method_names_in_sections_1(self): + args = { + 'template': '{{#upper}}{{{.}}} == {{{upper}}}{{/upper}}', + 'data': {'upper': 'foo'}, + } + + result = chevron.render(**args) + expected = 'foo == foo' + self.assertEqual(result, expected) + + # Another regression test for + # https://github.com/noahmorrison/chevron/issues/104 + def test_string_method_names_in_sections_2(self): + args = { + 'template': ( + '{{#a}}{{#b}}{{{b}}} ' + '{{#upper}}{{{upper}}}{{/upper}}{{/b}}{{/a}}' + ), + 'data': {'a': [{'upper': 'foo', 'b': 'x'}]}, + } + result = chevron.render(**args) + expected = 'x foo' + self.assertEqual(result, expected) + def test_get_key_not_in_dunder_dict_returns_attribute(self): class C: foo = "bar"