1# -*- coding: utf-8 -*- 2""" 3 jinja2.utils 4 ~~~~~~~~~~~~ 5 6 Utility functions. 7 8 :copyright: (c) 2010 by the Jinja Team. 9 :license: BSD, see LICENSE for more details. 10""" 11import re 12import sys 13import errno 14try: 15 from thread import allocate_lock 16except ImportError: 17 from dummy_thread import allocate_lock 18from collections import deque 19from itertools import imap 20 21 22_word_split_re = re.compile(r'(\s+)') 23_punctuation_re = re.compile( 24 '^(?P<lead>(?:%s)*)(?P<middle>.*?)(?P<trail>(?:%s)*)$' % ( 25 '|'.join(imap(re.escape, ('(', '<', '<'))), 26 '|'.join(imap(re.escape, ('.', ',', ')', '>', '\n', '>'))) 27 ) 28) 29_simple_email_re = re.compile(r'^\S+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9._-]+$') 30_striptags_re = re.compile(r'(<!--.*?-->|<[^>]*>)') 31_entity_re = re.compile(r'&([^;]+);') 32_letters = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' 33_digits = '0123456789' 34 35# special singleton representing missing values for the runtime 36missing = type('MissingType', (), {'__repr__': lambda x: 'missing'})() 37 38# internal code 39internal_code = set() 40 41 42# concatenate a list of strings and convert them to unicode. 43# unfortunately there is a bug in python 2.4 and lower that causes 44# unicode.join trash the traceback. 45_concat = u''.join 46try: 47 def _test_gen_bug(): 48 raise TypeError(_test_gen_bug) 49 yield None 50 _concat(_test_gen_bug()) 51except TypeError, _error: 52 if not _error.args or _error.args[0] is not _test_gen_bug: 53 def concat(gen): 54 try: 55 return _concat(list(gen)) 56 except: 57 # this hack is needed so that the current frame 58 # does not show up in the traceback. 59 exc_type, exc_value, tb = sys.exc_info() 60 raise exc_type, exc_value, tb.tb_next 61 else: 62 concat = _concat 63 del _test_gen_bug, _error 64 65 66# for python 2.x we create outselves a next() function that does the 67# basics without exception catching. 68try: 69 next = next 70except NameError: 71 def next(x): 72 return x.next() 73 74 75# if this python version is unable to deal with unicode filenames 76# when passed to encode we let this function encode it properly. 77# This is used in a couple of places. As far as Jinja is concerned 78# filenames are unicode *or* bytestrings in 2.x and unicode only in 79# 3.x because compile cannot handle bytes 80if sys.version_info < (3, 0): 81 def _encode_filename(filename): 82 if isinstance(filename, unicode): 83 return filename.encode('utf-8') 84 return filename 85else: 86 def _encode_filename(filename): 87 assert filename is None or isinstance(filename, str), \ 88 'filenames must be strings' 89 return filename 90 91from keyword import iskeyword as is_python_keyword 92 93 94# common types. These do exist in the special types module too which however 95# does not exist in IronPython out of the box. Also that way we don't have 96# to deal with implementation specific stuff here 97class _C(object): 98 def method(self): pass 99def _func(): 100 yield None 101FunctionType = type(_func) 102GeneratorType = type(_func()) 103MethodType = type(_C.method) 104CodeType = type(_C.method.func_code) 105try: 106 raise TypeError() 107except TypeError: 108 _tb = sys.exc_info()[2] 109 TracebackType = type(_tb) 110 FrameType = type(_tb.tb_frame) 111del _C, _tb, _func 112 113 114def contextfunction(f): 115 """This decorator can be used to mark a function or method context callable. 116 A context callable is passed the active :class:`Context` as first argument when 117 called from the template. This is useful if a function wants to get access 118 to the context or functions provided on the context object. For example 119 a function that returns a sorted list of template variables the current 120 template exports could look like this:: 121 122 @contextfunction 123 def get_exported_names(context): 124 return sorted(context.exported_vars) 125 """ 126 f.contextfunction = True 127 return f 128 129 130def evalcontextfunction(f): 131 """This decoraotr can be used to mark a function or method as an eval 132 context callable. This is similar to the :func:`contextfunction` 133 but instead of passing the context, an evaluation context object is 134 passed. For more information about the eval context, see 135 :ref:`eval-context`. 136 137 .. versionadded:: 2.4 138 """ 139 f.evalcontextfunction = True 140 return f 141 142 143def environmentfunction(f): 144 """This decorator can be used to mark a function or method as environment 145 callable. This decorator works exactly like the :func:`contextfunction` 146 decorator just that the first argument is the active :class:`Environment` 147 and not context. 148 """ 149 f.environmentfunction = True 150 return f 151 152 153def internalcode(f): 154 """Marks the function as internally used""" 155 internal_code.add(f.func_code) 156 return f 157 158 159def is_undefined(obj): 160 """Check if the object passed is undefined. This does nothing more than 161 performing an instance check against :class:`Undefined` but looks nicer. 162 This can be used for custom filters or tests that want to react to 163 undefined variables. For example a custom default filter can look like 164 this:: 165 166 def default(var, default=''): 167 if is_undefined(var): 168 return default 169 return var 170 """ 171 from jinja2.runtime import Undefined 172 return isinstance(obj, Undefined) 173 174 175def consume(iterable): 176 """Consumes an iterable without doing anything with it.""" 177 for event in iterable: 178 pass 179 180 181def clear_caches(): 182 """Jinja2 keeps internal caches for environments and lexers. These are 183 used so that Jinja2 doesn't have to recreate environments and lexers all 184 the time. Normally you don't have to care about that but if you are 185 messuring memory consumption you may want to clean the caches. 186 """ 187 from jinja2.environment import _spontaneous_environments 188 from jinja2.lexer import _lexer_cache 189 _spontaneous_environments.clear() 190 _lexer_cache.clear() 191 192 193def import_string(import_name, silent=False): 194 """Imports an object based on a string. This use useful if you want to 195 use import paths as endpoints or something similar. An import path can 196 be specified either in dotted notation (``xml.sax.saxutils.escape``) 197 or with a colon as object delimiter (``xml.sax.saxutils:escape``). 198 199 If the `silent` is True the return value will be `None` if the import 200 fails. 201 202 :return: imported object 203 """ 204 try: 205 if ':' in import_name: 206 module, obj = import_name.split(':', 1) 207 elif '.' in import_name: 208 items = import_name.split('.') 209 module = '.'.join(items[:-1]) 210 obj = items[-1] 211 else: 212 return __import__(import_name) 213 return getattr(__import__(module, None, None, [obj]), obj) 214 except (ImportError, AttributeError): 215 if not silent: 216 raise 217 218 219def open_if_exists(filename, mode='rb'): 220 """Returns a file descriptor for the filename if that file exists, 221 otherwise `None`. 222 """ 223 try: 224 return open(filename, mode) 225 except IOError, e: 226 if e.errno not in (errno.ENOENT, errno.EISDIR): 227 raise 228 229 230def object_type_repr(obj): 231 """Returns the name of the object's type. For some recognized 232 singletons the name of the object is returned instead. (For 233 example for `None` and `Ellipsis`). 234 """ 235 if obj is None: 236 return 'None' 237 elif obj is Ellipsis: 238 return 'Ellipsis' 239 if obj.__class__.__module__ == '__builtin__': 240 name = obj.__class__.__name__ 241 else: 242 name = obj.__class__.__module__ + '.' + obj.__class__.__name__ 243 return '%s object' % name 244 245 246def pformat(obj, verbose=False): 247 """Prettyprint an object. Either use the `pretty` library or the 248 builtin `pprint`. 249 """ 250 try: 251 from pretty import pretty 252 return pretty(obj, verbose=verbose) 253 except ImportError: 254 from pprint import pformat 255 return pformat(obj) 256 257 258def urlize(text, trim_url_limit=None, nofollow=False): 259 """Converts any URLs in text into clickable links. Works on http://, 260 https:// and www. links. Links can have trailing punctuation (periods, 261 commas, close-parens) and leading punctuation (opening parens) and 262 it'll still do the right thing. 263 264 If trim_url_limit is not None, the URLs in link text will be limited 265 to trim_url_limit characters. 266 267 If nofollow is True, the URLs in link text will get a rel="nofollow" 268 attribute. 269 """ 270 trim_url = lambda x, limit=trim_url_limit: limit is not None \ 271 and (x[:limit] + (len(x) >=limit and '...' 272 or '')) or x 273 words = _word_split_re.split(unicode(escape(text))) 274 nofollow_attr = nofollow and ' rel="nofollow"' or '' 275 for i, word in enumerate(words): 276 match = _punctuation_re.match(word) 277 if match: 278 lead, middle, trail = match.groups() 279 if middle.startswith('www.') or ( 280 '@' not in middle and 281 not middle.startswith('http://') and 282 len(middle) > 0 and 283 middle[0] in _letters + _digits and ( 284 middle.endswith('.org') or 285 middle.endswith('.net') or 286 middle.endswith('.com') 287 )): 288 middle = '<a href="http://%s"%s>%s</a>' % (middle, 289 nofollow_attr, trim_url(middle)) 290 if middle.startswith('http://') or \ 291 middle.startswith('https://'): 292 middle = '<a href="%s"%s>%s</a>' % (middle, 293 nofollow_attr, trim_url(middle)) 294 if '@' in middle and not middle.startswith('www.') and \ 295 not ':' in middle and _simple_email_re.match(middle): 296 middle = '<a href="mailto:%s">%s</a>' % (middle, middle) 297 if lead + middle + trail != word: 298 words[i] = lead + middle + trail 299 return u''.join(words) 300 301 302def generate_lorem_ipsum(n=5, html=True, min=20, max=100): 303 """Generate some lorem impsum for the template.""" 304 from jinja2.constants import LOREM_IPSUM_WORDS 305 from random import choice, randrange 306 words = LOREM_IPSUM_WORDS.split() 307 result = [] 308 309 for _ in xrange(n): 310 next_capitalized = True 311 last_comma = last_fullstop = 0 312 word = None 313 last = None 314 p = [] 315 316 # each paragraph contains out of 20 to 100 words. 317 for idx, _ in enumerate(xrange(randrange(min, max))): 318 while True: 319 word = choice(words) 320 if word != last: 321 last = word 322 break 323 if next_capitalized: 324 word = word.capitalize() 325 next_capitalized = False 326 # add commas 327 if idx - randrange(3, 8) > last_comma: 328 last_comma = idx 329 last_fullstop += 2 330 word += ',' 331 # add end of sentences 332 if idx - randrange(10, 20) > last_fullstop: 333 last_comma = last_fullstop = idx 334 word += '.' 335 next_capitalized = True 336 p.append(word) 337 338 # ensure that the paragraph ends with a dot. 339 p = u' '.join(p) 340 if p.endswith(','): 341 p = p[:-1] + '.' 342 elif not p.endswith('.'): 343 p += '.' 344 result.append(p) 345 346 if not html: 347 return u'\n\n'.join(result) 348 return Markup(u'\n'.join(u'<p>%s</p>' % escape(x) for x in result)) 349 350 351class Markup(unicode): 352 r"""Marks a string as being safe for inclusion in HTML/XML output without 353 needing to be escaped. This implements the `__html__` interface a couple 354 of frameworks and web applications use. :class:`Markup` is a direct 355 subclass of `unicode` and provides all the methods of `unicode` just that 356 it escapes arguments passed and always returns `Markup`. 357 358 The `escape` function returns markup objects so that double escaping can't 359 happen. If you want to use autoescaping in Jinja just enable the 360 autoescaping feature in the environment. 361 362 The constructor of the :class:`Markup` class can be used for three 363 different things: When passed an unicode object it's assumed to be safe, 364 when passed an object with an HTML representation (has an `__html__` 365 method) that representation is used, otherwise the object passed is 366 converted into a unicode string and then assumed to be safe: 367 368 >>> Markup("Hello <em>World</em>!") 369 Markup(u'Hello <em>World</em>!') 370 >>> class Foo(object): 371 ... def __html__(self): 372 ... return '<a href="#">foo</a>' 373 ... 374 >>> Markup(Foo()) 375 Markup(u'<a href="#">foo</a>') 376 377 If you want object passed being always treated as unsafe you can use the 378 :meth:`escape` classmethod to create a :class:`Markup` object: 379 380 >>> Markup.escape("Hello <em>World</em>!") 381 Markup(u'Hello <em>World</em>!') 382 383 Operations on a markup string are markup aware which means that all 384 arguments are passed through the :func:`escape` function: 385 386 >>> em = Markup("<em>%s</em>") 387 >>> em % "foo & bar" 388 Markup(u'<em>foo & bar</em>') 389 >>> strong = Markup("<strong>%(text)s</strong>") 390 >>> strong % {'text': '<blink>hacker here</blink>'} 391 Markup(u'<strong><blink>hacker here</blink></strong>') 392 >>> Markup("<em>Hello</em> ") + "<foo>" 393 Markup(u'<em>Hello</em> <foo>') 394 """ 395 __slots__ = () 396 397 def __new__(cls, base=u'', encoding=None, errors='strict'): 398 if hasattr(base, '__html__'): 399 base = base.__html__() 400 if encoding is None: 401 return unicode.__new__(cls, base) 402 return unicode.__new__(cls, base, encoding, errors) 403 404 def __html__(self): 405 return self 406 407 def __add__(self, other): 408 if hasattr(other, '__html__') or isinstance(other, basestring): 409 return self.__class__(unicode(self) + unicode(escape(other))) 410 return NotImplemented 411 412 def __radd__(self, other): 413 if hasattr(other, '__html__') or isinstance(other, basestring): 414 return self.__class__(unicode(escape(other)) + unicode(self)) 415 return NotImplemented 416 417 def __mul__(self, num): 418 if isinstance(num, (int, long)): 419 return self.__class__(unicode.__mul__(self, num)) 420 return NotImplemented 421 __rmul__ = __mul__ 422 423 def __mod__(self, arg): 424 if isinstance(arg, tuple): 425 arg = tuple(imap(_MarkupEscapeHelper, arg)) 426 else: 427 arg = _MarkupEscapeHelper(arg) 428 return self.__class__(unicode.__mod__(self, arg)) 429 430 def __repr__(self): 431 return '%s(%s)' % ( 432 self.__class__.__name__, 433 unicode.__repr__(self) 434 ) 435 436 def join(self, seq): 437 return self.__class__(unicode.join(self, imap(escape, seq))) 438 join.__doc__ = unicode.join.__doc__ 439 440 def split(self, *args, **kwargs): 441 return map(self.__class__, unicode.split(self, *args, **kwargs)) 442 split.__doc__ = unicode.split.__doc__ 443 444 def rsplit(self, *args, **kwargs): 445 return map(self.__class__, unicode.rsplit(self, *args, **kwargs)) 446 rsplit.__doc__ = unicode.rsplit.__doc__ 447 448 def splitlines(self, *args, **kwargs): 449 return map(self.__class__, unicode.splitlines(self, *args, **kwargs)) 450 splitlines.__doc__ = unicode.splitlines.__doc__ 451 452 def unescape(self): 453 r"""Unescape markup again into an unicode string. This also resolves 454 known HTML4 and XHTML entities: 455 456 >>> Markup("Main » <em>About</em>").unescape() 457 u'Main \xbb <em>About</em>' 458 """ 459 from jinja2.constants import HTML_ENTITIES 460 def handle_match(m): 461 name = m.group(1) 462 if name in HTML_ENTITIES: 463 return unichr(HTML_ENTITIES[name]) 464 try: 465 if name[:2] in ('#x', '#X'): 466 return unichr(int(name[2:], 16)) 467 elif name.startswith('#'): 468 return unichr(int(name[1:])) 469 except ValueError: 470 pass 471 return u'' 472 return _entity_re.sub(handle_match, unicode(self)) 473 474 def striptags(self): 475 r"""Unescape markup into an unicode string and strip all tags. This 476 also resolves known HTML4 and XHTML entities. Whitespace is 477 normalized to one: 478 479 >>> Markup("Main » <em>About</em>").striptags() 480 u'Main \xbb About' 481 """ 482 stripped = u' '.join(_striptags_re.sub('', self).split()) 483 return Markup(stripped).unescape() 484 485 @classmethod 486 def escape(cls, s): 487 """Escape the string. Works like :func:`escape` with the difference 488 that for subclasses of :class:`Markup` this function would return the 489 correct subclass. 490 """ 491 rv = escape(s) 492 if rv.__class__ is not cls: 493 return cls(rv) 494 return rv 495 496 def make_wrapper(name): 497 orig = getattr(unicode, name) 498 def func(self, *args, **kwargs): 499 args = _escape_argspec(list(args), enumerate(args)) 500 _escape_argspec(kwargs, kwargs.iteritems()) 501 return self.__class__(orig(self, *args, **kwargs)) 502 func.__name__ = orig.__name__ 503 func.__doc__ = orig.__doc__ 504 return func 505 506 for method in '__getitem__', 'capitalize', \ 507 'title', 'lower', 'upper', 'replace', 'ljust', \ 508 'rjust', 'lstrip', 'rstrip', 'center', 'strip', \ 509 'translate', 'expandtabs', 'swapcase', 'zfill': 510 locals()[method] = make_wrapper(method) 511 512 # new in python 2.5 513 if hasattr(unicode, 'partition'): 514 partition = make_wrapper('partition'), 515 rpartition = make_wrapper('rpartition') 516 517 # new in python 2.6 518 if hasattr(unicode, 'format'): 519 format = make_wrapper('format') 520 521 # not in python 3 522 if hasattr(unicode, '__getslice__'): 523 __getslice__ = make_wrapper('__getslice__') 524 525 del method, make_wrapper 526 527 528def _escape_argspec(obj, iterable): 529 """Helper for various string-wrapped functions.""" 530 for key, value in iterable: 531 if hasattr(value, '__html__') or isinstance(value, basestring): 532 obj[key] = escape(value) 533 return obj 534 535 536class _MarkupEscapeHelper(object): 537 """Helper for Markup.__mod__""" 538 539 def __init__(self, obj): 540 self.obj = obj 541 542 __getitem__ = lambda s, x: _MarkupEscapeHelper(s.obj[x]) 543 __str__ = lambda s: str(escape(s.obj)) 544 __unicode__ = lambda s: unicode(escape(s.obj)) 545 __repr__ = lambda s: str(escape(repr(s.obj))) 546 __int__ = lambda s: int(s.obj) 547 __float__ = lambda s: float(s.obj) 548 549 550class LRUCache(object): 551 """A simple LRU Cache implementation.""" 552 553 # this is fast for small capacities (something below 1000) but doesn't 554 # scale. But as long as it's only used as storage for templates this 555 # won't do any harm. 556 557 def __init__(self, capacity): 558 self.capacity = capacity 559 self._mapping = {} 560 self._queue = deque() 561 self._postinit() 562 563 def _postinit(self): 564 # alias all queue methods for faster lookup 565 self._popleft = self._queue.popleft 566 self._pop = self._queue.pop 567 if hasattr(self._queue, 'remove'): 568 self._remove = self._queue.remove 569 self._wlock = allocate_lock() 570 self._append = self._queue.append 571 572 def _remove(self, obj): 573 """Python 2.4 compatibility.""" 574 for idx, item in enumerate(self._queue): 575 if item == obj: 576 del self._queue[idx] 577 break 578 579 def __getstate__(self): 580 return { 581 'capacity': self.capacity, 582 '_mapping': self._mapping, 583 '_queue': self._queue 584 } 585 586 def __setstate__(self, d): 587 self.__dict__.update(d) 588 self._postinit() 589 590 def __getnewargs__(self): 591 return (self.capacity,) 592 593 def copy(self): 594 """Return an shallow copy of the instance.""" 595 rv = self.__class__(self.capacity) 596 rv._mapping.update(self._mapping) 597 rv._queue = deque(self._queue) 598 return rv 599 600 def get(self, key, default=None): 601 """Return an item from the cache dict or `default`""" 602 try: 603 return self[key] 604 except KeyError: 605 return default 606 607 def setdefault(self, key, default=None): 608 """Set `default` if the key is not in the cache otherwise 609 leave unchanged. Return the value of this key. 610 """ 611 try: 612 return self[key] 613 except KeyError: 614 self[key] = default 615 return default 616 617 def clear(self): 618 """Clear the cache.""" 619 self._wlock.acquire() 620 try: 621 self._mapping.clear() 622 self._queue.clear() 623 finally: 624 self._wlock.release() 625 626 def __contains__(self, key): 627 """Check if a key exists in this cache.""" 628 return key in self._mapping 629 630 def __len__(self): 631 """Return the current size of the cache.""" 632 return len(self._mapping) 633 634 def __repr__(self): 635 return '<%s %r>' % ( 636 self.__class__.__name__, 637 self._mapping 638 ) 639 640 def __getitem__(self, key): 641 """Get an item from the cache. Moves the item up so that it has the 642 highest priority then. 643 644 Raise an `KeyError` if it does not exist. 645 """ 646 rv = self._mapping[key] 647 if self._queue[-1] != key: 648 try: 649 self._remove(key) 650 except ValueError: 651 # if something removed the key from the container 652 # when we read, ignore the ValueError that we would 653 # get otherwise. 654 pass 655 self._append(key) 656 return rv 657 658 def __setitem__(self, key, value): 659 """Sets the value for an item. Moves the item up so that it 660 has the highest priority then. 661 """ 662 self._wlock.acquire() 663 try: 664 if key in self._mapping: 665 try: 666 self._remove(key) 667 except ValueError: 668 # __getitem__ is not locked, it might happen 669 pass 670 elif len(self._mapping) == self.capacity: 671 del self._mapping[self._popleft()] 672 self._append(key) 673 self._mapping[key] = value 674 finally: 675 self._wlock.release() 676 677 def __delitem__(self, key): 678 """Remove an item from the cache dict. 679 Raise an `KeyError` if it does not exist. 680 """ 681 self._wlock.acquire() 682 try: 683 del self._mapping[key] 684 try: 685 self._remove(key) 686 except ValueError: 687 # __getitem__ is not locked, it might happen 688 pass 689 finally: 690 self._wlock.release() 691 692 def items(self): 693 """Return a list of items.""" 694 result = [(key, self._mapping[key]) for key in list(self._queue)] 695 result.reverse() 696 return result 697 698 def iteritems(self): 699 """Iterate over all items.""" 700 return iter(self.items()) 701 702 def values(self): 703 """Return a list of all values.""" 704 return [x[1] for x in self.items()] 705 706 def itervalue(self): 707 """Iterate over all values.""" 708 return iter(self.values()) 709 710 def keys(self): 711 """Return a list of all keys ordered by most recent usage.""" 712 return list(self) 713 714 def iterkeys(self): 715 """Iterate over all keys in the cache dict, ordered by 716 the most recent usage. 717 """ 718 return reversed(tuple(self._queue)) 719 720 __iter__ = iterkeys 721 722 def __reversed__(self): 723 """Iterate over the values in the cache dict, oldest items 724 coming first. 725 """ 726 return iter(tuple(self._queue)) 727 728 __copy__ = copy 729 730 731# register the LRU cache as mutable mapping if possible 732try: 733 from collections import MutableMapping 734 MutableMapping.register(LRUCache) 735except ImportError: 736 pass 737 738 739class Cycler(object): 740 """A cycle helper for templates.""" 741 742 def __init__(self, *items): 743 if not items: 744 raise RuntimeError('at least one item has to be provided') 745 self.items = items 746 self.reset() 747 748 def reset(self): 749 """Resets the cycle.""" 750 self.pos = 0 751 752 @property 753 def current(self): 754 """Returns the current item.""" 755 return self.items[self.pos] 756 757 def next(self): 758 """Goes one item ahead and returns it.""" 759 rv = self.current 760 self.pos = (self.pos + 1) % len(self.items) 761 return rv 762 763 764class Joiner(object): 765 """A joining helper for templates.""" 766 767 def __init__(self, sep=u', '): 768 self.sep = sep 769 self.used = False 770 771 def __call__(self): 772 if not self.used: 773 self.used = True 774 return u'' 775 return self.sep 776 777 778# we have to import it down here as the speedups module imports the 779# markup type which is define above. 780try: 781 from jinja2._speedups import escape, soft_unicode 782except ImportError: 783 def escape(s): 784 """Convert the characters &, <, >, ' and " in string s to HTML-safe 785 sequences. Use this if you need to display text that might contain 786 such characters in HTML. Marks return value as markup string. 787 """ 788 if hasattr(s, '__html__'): 789 return s.__html__() 790 return Markup(unicode(s) 791 .replace('&', '&') 792 .replace('>', '>') 793 .replace('<', '<') 794 .replace("'", ''') 795 .replace('"', '"') 796 ) 797 798 def soft_unicode(s): 799 """Make a string unicode if it isn't already. That way a markup 800 string is not converted back to unicode. 801 """ 802 if not isinstance(s, unicode): 803 s = unicode(s) 804 return s 805 806 807# partials 808try: 809 from functools import partial 810except ImportError: 811 class partial(object): 812 def __init__(self, _func, *args, **kwargs): 813 self._func = _func 814 self._args = args 815 self._kwargs = kwargs 816 def __call__(self, *args, **kwargs): 817 kwargs.update(self._kwargs) 818 return self._func(*(self._args + args), **kwargs) 819