From 5abf7ebfc0f2196ae3cc0cfd1141090052ea6be2 Mon Sep 17 00:00:00 2001 From: Guido Berhoerster Date: Tue, 28 Aug 2018 15:43:52 +0200 Subject: [PATCH] Use more pythonic type checking Use isinstance() and abstract base classes for checking available functionality instead of strictly checking for base types. Among other things, this allows for rendering any non-string sequences, iterators or generators as lists (resolves #33) and for passing in any callable instead of just plain functions. --- chevron/renderer.py | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/chevron/renderer.py b/chevron/renderer.py index 3c284d1..c66471d 100644 --- a/chevron/renderer.py +++ b/chevron/renderer.py @@ -1,5 +1,9 @@ # -*- coding: utf-8 -*- +try: + from collections.abc import Sequence, Iterator, Callable +except ImportError: # python 2 + from collections import Sequence, Iterator, Callable try: from .tokenizer import tokenize except (ValueError, SystemError): # python 2 @@ -9,12 +13,16 @@ except (ValueError, SystemError): # python 2 import sys if sys.version_info[0] == 3: python3 = True + unicode_type = str + string_type = str def unicode(x, y): return x else: # python 2 python3 = False + unicode_type = unicode + string_type = basestring # @@ -155,10 +163,9 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache', A string containing the rendered template. """ - function = type(render) - - # If the template is a list - if type(template) is list: + # If the template is a seqeuence but not derived from a string + if isinstance(template, Sequence) and \ + not isinstance(template, string_type): # Then we don't need to tokenize it # But it does need to be a generator tokens = (token for token in template) @@ -194,7 +201,7 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache', # If we're a literal tag elif tag == 'literal': # Add padding to the key and add it to the output - if type(key) != unicode: + if not isinstance(key, unicode_type): key = unicode(key, 'utf-8') output += key.replace('\n', '\n' + (' ' * padding)) @@ -207,7 +214,7 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache', # (inverted tags do this) # then get the un-coerced object (next in the stack) thing = scopes[1] - if type(thing) != unicode: + if not isinstance(thing, unicode_type): thing = unicode(str(thing), 'utf-8') output += _html_escape(thing) @@ -215,7 +222,7 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache', elif tag == 'no escape': # Just lookup the key and add it thing = _get_key(key, scopes) - if type(thing) != unicode: + if not isinstance(thing, unicode_type): thing = unicode(str(thing), 'utf-8') output += thing @@ -226,7 +233,7 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache', # If the scope is a callable (as described in # https://mustache.github.io/mustache.5.html) - if type(scope) is function: + if isinstance(scope, Callable): # Generate template text from tags text = unicode('', 'utf-8') @@ -268,8 +275,10 @@ def render(template='', data={}, partials_path='.', partials_ext='mustache', else: # python 2 output += rend.decode('utf-8') - # If the scope is a list - elif type(scope) is list: + # If the scope is a sequence, an iterator or generator but not + # derived from a string + elif isinstance(scope, (Sequence, Iterator)) and \ + not isinstance(scope, string_type): # Then we need to do some looping # Gather up all the tags inside the section -- 2.47.3