1from __future__ import absolute_import 2from .utils import odict 3import types 4import six 5from itertools import chain 6 7try: 8 from collections import Mapping as MappingType 9except ImportError: 10 import UserDict 11 MappingType = (UserDict.UserDict, UserDict.DictMixin, dict) 12 13def flatten(l, ltypes=(list, tuple)): 14 ltype = type(l) 15 l = list(l) 16 i = 0 17 while i < len(l): 18 while isinstance(l[i], ltypes): 19 if not l[i]: 20 l.pop(i) 21 i -= 1 22 break 23 else: 24 l[i:i + 1] = l[i] 25 i += 1 26 return ltype(l) 27 28def escape(s): 29 """Convert the characters &, <, >, ' and " in string s to HTML-safe 30 sequences. Use this if you need to display text that might contain 31 such characters in HTML. Marks return value as markup string. 32 """ 33 if hasattr(s, '__html__'): 34 return s.__html__() 35 if isinstance(s, six.binary_type): 36 s = six.text_type(str(s), 'utf8') 37 elif isinstance(s, six.text_type): 38 s = s 39 else: 40 s = str(s) 41 42 return (s 43 .replace('&', '&') 44 .replace('>', '>') 45 .replace('<', '<') 46 .replace("'", ''') 47 .replace('"', '"') 48 ) 49 50def attrs (attrs=[],terse=False, undefined=None): 51 buf = [] 52 if bool(attrs): 53 buf.append(u'') 54 for k,v in attrs: 55 if undefined is not None and isinstance(v, undefined): 56 continue 57 if v!=None and (v!=False or type(v)!=bool): 58 if k=='class' and isinstance(v, (list, tuple)): 59 v = u' '.join(map(str,flatten(v))) 60 t = v==True and type(v)==bool 61 if t and not terse: v=k 62 buf.append(u'%s'%k if terse and t else u'%s="%s"'%(k,escape(v))) 63 return u' '.join(buf) 64 65 66def is_mapping(value): 67 return isinstance(value, MappingType) 68 69 70def is_iterable(ob): 71 if isinstance(ob, six.string_types): 72 return False 73 try: 74 iter(ob) 75 return True 76 except TypeError: 77 return False 78 79 80def get_cardinality(ob): 81 if isinstance(ob, six.string_types): 82 return 1 83 try: 84 return len(ob) 85 except TypeError: 86 return 1 87 88 89def iteration(obj, num_keys): 90 """ 91 Jade iteration supports "for 'value' [, key]?" iteration only. 92 PyJade has implicitly supported value unpacking instead, without 93 the list indexes. Trying to not break existing code, the following 94 rules are applied: 95 96 1. If the object is a mapping type, return it as-is, and assume 97 the caller has the correct set of keys defined. 98 99 2. If the object's values are iterable (and not string-like): 100 a. If the number of keys matches the cardinality of the object's 101 values, return the object as-is. 102 b. If the number of keys is one more than the cardinality of 103 values, return a list of [v(0), v(1), ... v(n), index] 104 105 3. Else the object's values are not iterable, or are string like: 106 a. if there's only one key, return the list 107 b. otherwise return a list of (value,index) tuples 108 109 """ 110 111 # If the object is a mapping type, return it as-is 112 if is_mapping(obj): 113 return obj 114 115 _marker = [] 116 117 iter_obj = iter(obj) 118 head = next(iter_obj, _marker) 119 iter_obj = chain([head], iter_obj) 120 121 if head is _marker: 122 # Empty list 123 return [] 124 125 if is_iterable(head): 126 if num_keys == get_cardinality(head) + 1: 127 return (tuple(item) + (ix,) for ix, item in enumerate(iter_obj)) 128 else: 129 return iter_obj 130 131 elif num_keys == 2: 132 return ((item, ix) for ix, item in enumerate(iter_obj)) 133 134 else: 135 return iter_obj 136