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 10from _functools import partial, reduce 11 12# update_wrapper() and wraps() are tools to help write 13# wrapper functions that can handle naive introspection 14 15WRAPPER_ASSIGNMENTS = ('__module__', '__name__', '__doc__') 16WRAPPER_UPDATES = ('__dict__',) 17def 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 39def 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 53def 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 ('__ne__', lambda self, other: not self == other), 59 ('__ge__', lambda self, other: not self < other)], 60 '__le__': [('__ge__', lambda self, other: not self <= other or self == other), 61 ('__lt__', lambda self, other: self <= other and not self == other), 62 ('__ne__', lambda self, other: not self == other), 63 ('__gt__', lambda self, other: not self <= other)], 64 '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), 65 ('__ge__', lambda self, other: self > other or self == other), 66 ('__ne__', lambda self, other: not self == other), 67 ('__le__', lambda self, other: not self > other)], 68 '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), 69 ('__gt__', lambda self, other: self >= other and not self == other), 70 ('__ne__', lambda self, other: not self == other), 71 ('__lt__', lambda self, other: not self >= other)] 72 } 73 defined_methods = set(dir(cls)) 74 roots = defined_methods & set(convert) 75 if not roots: 76 raise ValueError('must define at least one ordering operation: < > <= >=') 77 root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ 78 for opname, opfunc in convert[root]: 79 if opname not in defined_methods: 80 opfunc.__name__ = opname 81 opfunc.__doc__ = getattr(int, opname).__doc__ 82 setattr(cls, opname, opfunc) 83 return cls 84 85def cmp_to_key(mycmp): 86 """Convert a cmp= function into a key= function""" 87 class K(object): 88 __slots__ = ['obj'] 89 def __init__(self, obj, *args): 90 self.obj = obj 91 def __lt__(self, other): 92 return mycmp(self.obj, other.obj) < 0 93 def __gt__(self, other): 94 return mycmp(self.obj, other.obj) > 0 95 def __eq__(self, other): 96 return mycmp(self.obj, other.obj) == 0 97 def __le__(self, other): 98 return mycmp(self.obj, other.obj) <= 0 99 def __ge__(self, other): 100 return mycmp(self.obj, other.obj) >= 0 101 def __ne__(self, other): 102 return mycmp(self.obj, other.obj) != 0 103 def __hash__(self): 104 raise TypeError('hash not implemented') 105 return K 106