1 """functools.py - Tools for working with functions and callable objects 2 """ 3 # Python module wrapper for _functools C module 4 # to allow utilities written in Python to be added 5 # to the functools module. 6 # Written by Nick Coghlan <ncoghlan at gmail.com> 7 # Copyright (C) 2006 Python Software Foundation. 8 # See C source code for _functools credits/copyright 9 10 from _functools import partial, reduce 11 12 # update_wrapper() and wraps() are tools to help write 13 # wrapper functions that can handle naive introspection 14 15 WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') 16 WRAPPER_UPDATES = ('__dict__',) 17 def update_wrapper(wrapper, 18 wrapped, 19 assigned = WRAPPER_ASSIGNMENTS, 20 updated = WRAPPER_UPDATES): 21 """Update a wrapper function to look like the wrapped function 22 23 wrapper is the function to be updated 24 wrapped is the original function 25 assigned is a tuple naming the attributes assigned directly 26 from the wrapped function to the wrapper function (defaults to 27 functools.WRAPPER_ASSIGNMENTS) 28 updated is a tuple naming the attributes of the wrapper that 29 are updated with the corresponding attribute from the wrapped 30 function (defaults to functools.WRAPPER_UPDATES) 31 """ 32 for attr in assigned: 33 setattr(wrapper, attr, getattr(wrapped, attr)) 34 for attr in updated: 35 getattr(wrapper, attr).update(getattr(wrapped, attr, {})) 36 # Return the wrapper so this can be used as a decorator via partial() 37 return wrapper 38 39 def wraps(wrapped, 40 assigned = WRAPPER_ASSIGNMENTS, 41 updated = WRAPPER_UPDATES): 42 """Decorator factory to apply update_wrapper() to a wrapper function 43 44 Returns a decorator that invokes update_wrapper() with the decorated 45 function as the wrapper argument and the arguments to wraps() as the 46 remaining arguments. Default arguments are as for update_wrapper(). 47 This is a convenience function to simplify applying partial() to 48 update_wrapper(). 49 """ 50 return partial(update_wrapper, wrapped=wrapped, 51 assigned=assigned, updated=updated) 52 53 def total_ordering(cls): 54 """Class decorator that fills in missing ordering methods""" 55 convert = { 56 '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), 57 ('__le__', lambda self, other: self < other or self == other), 58 ('__ge__', lambda self, other: not self < other)], 59 '__le__': [('__ge__', lambda self, other: not self <= other or self == other), 60 ('__lt__', lambda self, other: self <= other and not self == other), 61 ('__gt__', lambda self, other: not self <= other)], 62 '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), 63 ('__ge__', lambda self, other: self > other or self == other), 64 ('__le__', lambda self, other: not self > other)], 65 '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), 66 ('__gt__', lambda self, other: self >= other and not self == other), 67 ('__lt__', lambda self, other: not self >= other)] 68 } 69 roots = set(dir(cls)) & set(convert) 70 if not roots: 71 raise ValueError('must define at least one ordering operation: < > <= >=') 72 root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ 73 for opname, opfunc in convert[root]: 74 if opname not in roots: 75 opfunc.__name__ = opname 76 opfunc.__doc__ = getattr(int, opname).__doc__ 77 setattr(cls, opname, opfunc) 78 return cls 79 80 def cmp_to_key(mycmp): 81 """Convert a cmp= function into a key= function""" 82 class K(object): 83 __slots__ = ['obj'] 84 def __init__(self, obj, *args): 85 self.obj = obj 86 def __lt__(self, other): 87 return mycmp(self.obj, other.obj) < 0 88 def __gt__(self, other): 89 return mycmp(self.obj, other.obj) > 0 90 def __eq__(self, other): 91 return mycmp(self.obj, other.obj) == 0 92 def __le__(self, other): 93 return mycmp(self.obj, other.obj) <= 0 94 def __ge__(self, other): 95 return mycmp(self.obj, other.obj) >= 0 96 def __ne__(self, other): 97 return mycmp(self.obj, other.obj) != 0 98 def __hash__(self): 99 raise TypeError('hash not implemented') 100 return K 101