filters.py (11681B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\jinja2\filters.py 2 import re 3 import math 4 from random import choice 5 from operator import itemgetter 6 from itertools import imap, groupby 7 from jinja2.utils import Markup, escape, pformat, urlize, soft_unicode, unicode_urlencode 8 from jinja2.runtime import Undefined 9 from jinja2.exceptions import FilterArgumentError 10 _word_re = re.compile('\\w+(?u)') 11 12 def contextfilter(f): 13 f.contextfilter = True 14 return f 15 16 17 def evalcontextfilter(f): 18 f.evalcontextfilter = True 19 return f 20 21 22 def environmentfilter(f): 23 f.environmentfilter = True 24 return f 25 26 27 def make_attrgetter(environment, attribute): 28 if not isinstance(attribute, basestring) or '.' not in attribute: 29 return lambda x: environment.getitem(x, attribute) 30 attribute = attribute.split('.') 31 32 def attrgetter(item): 33 for part in attribute: 34 item = environment.getitem(item, part) 35 36 return item 37 38 return attrgetter 39 40 41 def do_forceescape(value): 42 if hasattr(value, '__html__'): 43 value = value.__html__() 44 return escape(unicode(value)) 45 46 47 def do_urlencode(value): 48 itemiter = None 49 if isinstance(value, dict): 50 itemiter = value.iteritems() 51 elif not isinstance(value, basestring): 52 try: 53 itemiter = iter(value) 54 except TypeError: 55 pass 56 57 if itemiter is None: 58 return unicode_urlencode(value) 59 return u'&'.join((unicode_urlencode(k) + '=' + unicode_urlencode(v) for k, v in itemiter)) 60 61 62 @evalcontextfilter 63 def do_replace(eval_ctx, s, old, new, count = None): 64 if count is None: 65 count = -1 66 if not eval_ctx.autoescape: 67 return unicode(s).replace(unicode(old), unicode(new), count) 68 if hasattr(old, '__html__') or hasattr(new, '__html__') and not hasattr(s, '__html__'): 69 s = escape(s) 70 else: 71 s = soft_unicode(s) 72 return s.replace(soft_unicode(old), soft_unicode(new), count) 73 74 75 def do_upper(s): 76 return soft_unicode(s).upper() 77 78 79 def do_lower(s): 80 return soft_unicode(s).lower() 81 82 83 @evalcontextfilter 84 def do_xmlattr(_eval_ctx, d, autospace = True): 85 rv = u' '.join((u'%s="%s"' % (escape(key), escape(value)) for key, value in d.iteritems() if value is not None and not isinstance(value, Undefined))) 86 if autospace and rv: 87 rv = u' ' + rv 88 if _eval_ctx.autoescape: 89 rv = Markup(rv) 90 return rv 91 92 93 def do_capitalize(s): 94 return soft_unicode(s).capitalize() 95 96 97 def do_title(s): 98 return soft_unicode(s).title() 99 100 101 def do_dictsort(value, case_sensitive = False, by = 'key'): 102 if by == 'key': 103 pos = 0 104 elif by == 'value': 105 pos = 1 106 else: 107 raise FilterArgumentError('You can only sort by either "key" or "value"') 108 109 def sort_func(item): 110 value = item[pos] 111 if isinstance(value, basestring) and not case_sensitive: 112 value = value.lower() 113 return value 114 115 return sorted(value.items(), key=sort_func) 116 117 118 @environmentfilter 119 def do_sort(environment, value, reverse = False, case_sensitive = False, attribute = None): 120 if not case_sensitive: 121 122 def sort_func(item): 123 if isinstance(item, basestring): 124 item = item.lower() 125 return item 126 127 else: 128 sort_func = None 129 if attribute is not None: 130 getter = make_attrgetter(environment, attribute) 131 132 def sort_func(item, processor = sort_func or (lambda x: x)): 133 return processor(getter(item)) 134 135 return sorted(value, key=sort_func, reverse=reverse) 136 137 138 def do_default(value, default_value = u'', boolean = False): 139 if boolean and not value or isinstance(value, Undefined): 140 return default_value 141 return value 142 143 144 @evalcontextfilter 145 def do_join(eval_ctx, value, d = u'', attribute = None): 146 if attribute is not None: 147 value = imap(make_attrgetter(eval_ctx.environment, attribute), value) 148 if not eval_ctx.autoescape: 149 return unicode(d).join(imap(unicode, value)) 150 if not hasattr(d, '__html__'): 151 value = list(value) 152 do_escape = False 153 for idx, item in enumerate(value): 154 if hasattr(item, '__html__'): 155 do_escape = True 156 else: 157 value[idx] = unicode(item) 158 159 if do_escape: 160 d = escape(d) 161 else: 162 d = unicode(d) 163 return d.join(value) 164 return soft_unicode(d).join(imap(soft_unicode, value)) 165 166 167 def do_center(value, width = 80): 168 return unicode(value).center(width) 169 170 171 @environmentfilter 172 def do_first(environment, seq): 173 try: 174 return iter(seq).next() 175 except StopIteration: 176 return environment.undefined('No first item, sequence was empty.') 177 178 179 @environmentfilter 180 def do_last(environment, seq): 181 try: 182 return iter(reversed(seq)).next() 183 except StopIteration: 184 return environment.undefined('No last item, sequence was empty.') 185 186 187 @environmentfilter 188 def do_random(environment, seq): 189 try: 190 return choice(seq) 191 except IndexError: 192 return environment.undefined('No random item, sequence was empty.') 193 194 195 def do_filesizeformat(value, binary = False): 196 bytes = float(value) 197 base = binary and 1024 or 1000 198 prefixes = [binary and 'KiB' or 'kB', 199 binary and 'MiB' or 'MB', 200 binary and 'GiB' or 'GB', 201 binary and 'TiB' or 'TB', 202 binary and 'PiB' or 'PB', 203 binary and 'EiB' or 'EB', 204 binary and 'ZiB' or 'ZB', 205 binary and 'YiB' or 'YB'] 206 if bytes == 1: 207 return '1 Byte' 208 elif bytes < base: 209 return '%d Bytes' % bytes 210 else: 211 for i, prefix in enumerate(prefixes): 212 unit = base ** (i + 2) 213 if bytes < unit: 214 return '%.1f %s' % (base * bytes / unit, prefix) 215 216 return '%.1f %s' % (base * bytes / unit, prefix) 217 218 219 def do_pprint(value, verbose = False): 220 return pformat(value, verbose=verbose) 221 222 223 @evalcontextfilter 224 def do_urlize(eval_ctx, value, trim_url_limit = None, nofollow = False): 225 rv = urlize(value, trim_url_limit, nofollow) 226 if eval_ctx.autoescape: 227 rv = Markup(rv) 228 return rv 229 230 231 def do_indent(s, width = 4, indentfirst = False): 232 indention = u' ' * width 233 rv = (u'\n' + indention).join(s.splitlines()) 234 if indentfirst: 235 rv = indention + rv 236 return rv 237 238 239 def do_truncate(s, length = 255, killwords = False, end = '...'): 240 if len(s) <= length: 241 return s 242 if killwords: 243 return s[:length] + end 244 words = s.split(' ') 245 result = [] 246 m = 0 247 for word in words: 248 m += len(word) + 1 249 if m > length: 250 break 251 result.append(word) 252 253 result.append(end) 254 return u' '.join(result) 255 256 257 @environmentfilter 258 def do_wordwrap(environment, s, width = 79, break_long_words = True): 259 import textwrap 260 return environment.newline_sequence.join(textwrap.wrap(s, width=width, expand_tabs=False, replace_whitespace=False, break_long_words=break_long_words)) 261 262 263 def do_wordcount(s): 264 return len(_word_re.findall(s)) 265 266 267 def do_int(value, default = 0): 268 try: 269 return int(value) 270 except (TypeError, ValueError): 271 try: 272 return int(float(value)) 273 except (TypeError, ValueError): 274 return default 275 276 277 def do_float(value, default = 0.0): 278 try: 279 return float(value) 280 except (TypeError, ValueError): 281 return default 282 283 284 def do_format(value, *args, **kwargs): 285 if args and kwargs: 286 raise FilterArgumentError("can't handle positional and keyword arguments at the same time") 287 return soft_unicode(value) % (kwargs or args) 288 289 290 def do_trim(value): 291 return soft_unicode(value).strip() 292 293 294 def do_striptags(value): 295 if hasattr(value, '__html__'): 296 value = value.__html__() 297 return Markup(unicode(value)).striptags() 298 299 300 def do_slice(value, slices, fill_with = None): 301 seq = list(value) 302 length = len(seq) 303 items_per_slice = length // slices 304 slices_with_extra = length % slices 305 offset = 0 306 for slice_number in xrange(slices): 307 start = offset + slice_number * items_per_slice 308 if slice_number < slices_with_extra: 309 offset += 1 310 end = offset + (slice_number + 1) * items_per_slice 311 tmp = seq[start:end] 312 if fill_with is not None and slice_number >= slices_with_extra: 313 tmp.append(fill_with) 314 yield tmp 315 316 317 def do_batch(value, linecount, fill_with = None): 318 result = [] 319 tmp = [] 320 for item in value: 321 if len(tmp) == linecount: 322 yield tmp 323 tmp = [] 324 tmp.append(item) 325 326 if tmp: 327 if fill_with is not None and len(tmp) < linecount: 328 tmp += [fill_with] * (linecount - len(tmp)) 329 yield tmp 330 331 332 def do_round(value, precision = 0, method = 'common'): 333 if method not in ('common', 'ceil', 'floor'): 334 raise FilterArgumentError('method must be common, ceil or floor') 335 if method == 'common': 336 return round(value, precision) 337 func = getattr(math, method) 338 return func(value * 10 ** precision) / 10 ** precision 339 340 341 @environmentfilter 342 def do_groupby(environment, value, attribute): 343 expr = make_attrgetter(environment, attribute) 344 return sorted(map(_GroupTuple, groupby(sorted(value, key=expr), expr))) 345 346 347 class _GroupTuple(tuple): 348 __slots__ = () 349 grouper = property(itemgetter(0)) 350 list = property(itemgetter(1)) 351 352 def __new__(cls, (key, value)): 353 return tuple.__new__(cls, (key, list(value))) 354 355 356 @environmentfilter 357 def do_sum(environment, iterable, attribute = None, start = 0): 358 if attribute is not None: 359 iterable = imap(make_attrgetter(environment, attribute), iterable) 360 return sum(iterable, start) 361 362 363 def do_list(value): 364 return list(value) 365 366 367 def do_mark_safe(value): 368 return Markup(value) 369 370 371 def do_mark_unsafe(value): 372 return unicode(value) 373 374 375 def do_reverse(value): 376 if isinstance(value, basestring): 377 return value[::-1] 378 try: 379 return reversed(value) 380 except TypeError: 381 try: 382 rv = list(value) 383 rv.reverse() 384 return rv 385 except TypeError: 386 raise FilterArgumentError('argument must be iterable') 387 388 389 @environmentfilter 390 def do_attr(environment, obj, name): 391 try: 392 name = str(name) 393 except UnicodeError: 394 pass 395 else: 396 try: 397 value = getattr(obj, name) 398 except AttributeError: 399 pass 400 else: 401 if environment.sandboxed and not environment.is_safe_attribute(obj, name, value): 402 return environment.unsafe_undefined(obj, name) 403 return value 404 405 return environment.undefined(obj=obj, name=name) 406 407 408 FILTERS = {'attr': do_attr, 409 'replace': do_replace, 410 'upper': do_upper, 411 'lower': do_lower, 412 'escape': escape, 413 'e': escape, 414 'forceescape': do_forceescape, 415 'capitalize': do_capitalize, 416 'title': do_title, 417 'default': do_default, 418 'd': do_default, 419 'join': do_join, 420 'count': len, 421 'dictsort': do_dictsort, 422 'sort': do_sort, 423 'length': len, 424 'reverse': do_reverse, 425 'center': do_center, 426 'indent': do_indent, 427 'title': do_title, 428 'capitalize': do_capitalize, 429 'first': do_first, 430 'last': do_last, 431 'random': do_random, 432 'filesizeformat': do_filesizeformat, 433 'pprint': do_pprint, 434 'truncate': do_truncate, 435 'wordwrap': do_wordwrap, 436 'wordcount': do_wordcount, 437 'int': do_int, 438 'float': do_float, 439 'string': soft_unicode, 440 'list': do_list, 441 'urlize': do_urlize, 442 'format': do_format, 443 'trim': do_trim, 444 'striptags': do_striptags, 445 'slice': do_slice, 446 'batch': do_batch, 447 'sum': do_sum, 448 'abs': abs, 449 'round': do_round, 450 'groupby': do_groupby, 451 'safe': do_mark_safe, 452 'xmlattr': do_xmlattr, 453 'urlencode': do_urlencode}