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