From 2a9dcdeb8fefae44de19a94d09bffb44eadd9e13 Mon Sep 17 00:00:00 2001 From: Thomas Hebb Date: Fri, 22 May 2020 23:24:22 -0400 Subject: [PATCH] Allow field access for namedtuple in Python 3 `namedtuple` in Python 3 does not provide a `__dict__` attribute. See https://bugs.python.org/issue24931 for rationale why. As such, if we want to render keys in `namedtuple`s, we need to use its [`_asdict`][1] method. [1]: https://docs.python.org/3/library/collections.html#collections.somenamedtuple._asdict --- chevron/renderer.py | 13 +++++++++---- test_spec.py | 13 +++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) diff --git a/chevron/renderer.py b/chevron/renderer.py index 99415da..dfc2a40 100644 --- a/chevron/renderer.py +++ b/chevron/renderer.py @@ -66,11 +66,16 @@ def _get_key(key, scopes): scope = scope[child] except (TypeError, AttributeError): try: - # Try the dictionary (Complex types) - scope = scope.__dict__[child] + # Try namedtuple (which does not have __dict__ in + # Python 3: https://bugs.python.org/issue24931) + scope = scope._asdict()[child] except (TypeError, AttributeError): - # Try as a list - scope = scope[int(child)] + try: + # Try the dictionary (Complex types) + scope = scope.__dict__[child] + except (TypeError, AttributeError): + # Try as a list + 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 56bdea6..d064ffe 100755 --- a/test_spec.py +++ b/test_spec.py @@ -1,6 +1,7 @@ #!/usr/bin/python # -*- coding: utf-8 -*- +import collections import unittest import os import json @@ -435,6 +436,18 @@ class ExpandedCoverage(unittest.TestCase): self.assertEqual(result, expected) + def test_namedtuple_data(self): + NT = collections.namedtuple('NT', ['foo', 'bar']) + args = { + 'template': '{{foo}} {{bar}}', + 'data': NT('hello', 'world') + } + + result = chevron.render(**args) + expected = 'hello world' + + self.assertEqual(result, expected) + # Run unit tests from command line if __name__ == "__main__": -- 2.47.3