functools.py 4.4 KB

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