functional.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. import copy
  2. import operator
  3. from functools import wraps
  4. import sys
  5. import warnings
  6. from django.utils import six
  7. from django.utils.deprecation import RemovedInDjango19Warning
  8. from django.utils.six.moves import copyreg
  9. # You can't trivially replace this with `functools.partial` because this binds
  10. # to classes and returns bound instances, whereas functools.partial (on
  11. # CPython) is a type and its instances don't bind.
  12. def curry(_curried_func, *args, **kwargs):
  13. def _curried(*moreargs, **morekwargs):
  14. return _curried_func(*(args + moreargs), **dict(kwargs, **morekwargs))
  15. return _curried
  16. def memoize(func, cache, num_args):
  17. """
  18. Wrap a function so that results for any argument tuple are stored in
  19. 'cache'. Note that the args to the function must be usable as dictionary
  20. keys.
  21. Only the first num_args are considered when creating the key.
  22. """
  23. warnings.warn("memoize wrapper is deprecated and will be removed in "
  24. "Django 1.9. Use django.utils.lru_cache instead.",
  25. RemovedInDjango19Warning, stacklevel=2)
  26. @wraps(func)
  27. def wrapper(*args):
  28. mem_args = args[:num_args]
  29. if mem_args in cache:
  30. return cache[mem_args]
  31. result = func(*args)
  32. cache[mem_args] = result
  33. return result
  34. return wrapper
  35. class cached_property(object):
  36. """
  37. Decorator that converts a method with a single self argument into a
  38. property cached on the instance.
  39. """
  40. def __init__(self, func):
  41. self.func = func
  42. def __get__(self, instance, type=None):
  43. if instance is None:
  44. return self
  45. res = instance.__dict__[self.func.__name__] = self.func(instance)
  46. return res
  47. class Promise(object):
  48. """
  49. This is just a base class for the proxy class created in
  50. the closure of the lazy function. It can be used to recognize
  51. promises in code.
  52. """
  53. pass
  54. def lazy(func, *resultclasses):
  55. """
  56. Turns any callable into a lazy evaluated callable. You need to give result
  57. classes or types -- at least one is needed so that the automatic forcing of
  58. the lazy evaluation code is triggered. Results are not memoized; the
  59. function is evaluated on every access.
  60. """
  61. @total_ordering
  62. class __proxy__(Promise):
  63. """
  64. Encapsulate a function call and act as a proxy for methods that are
  65. called on the result of that function. The function is not evaluated
  66. until one of the methods on the result is called.
  67. """
  68. __dispatch = None
  69. def __init__(self, args, kw):
  70. self.__args = args
  71. self.__kw = kw
  72. if self.__dispatch is None:
  73. self.__prepare_class__()
  74. def __reduce__(self):
  75. return (
  76. _lazy_proxy_unpickle,
  77. (func, self.__args, self.__kw) + resultclasses
  78. )
  79. @classmethod
  80. def __prepare_class__(cls):
  81. cls.__dispatch = {}
  82. for resultclass in resultclasses:
  83. cls.__dispatch[resultclass] = {}
  84. for type_ in reversed(resultclass.mro()):
  85. for (k, v) in type_.__dict__.items():
  86. # All __promise__ return the same wrapper method, but
  87. # they also do setup, inserting the method into the
  88. # dispatch dict.
  89. meth = cls.__promise__(resultclass, k, v)
  90. if hasattr(cls, k):
  91. continue
  92. setattr(cls, k, meth)
  93. cls._delegate_bytes = bytes in resultclasses
  94. cls._delegate_text = six.text_type in resultclasses
  95. assert not (cls._delegate_bytes and cls._delegate_text), "Cannot call lazy() with both bytes and text return types."
  96. if cls._delegate_text:
  97. if six.PY3:
  98. cls.__str__ = cls.__text_cast
  99. else:
  100. cls.__unicode__ = cls.__text_cast
  101. elif cls._delegate_bytes:
  102. if six.PY3:
  103. cls.__bytes__ = cls.__bytes_cast
  104. else:
  105. cls.__str__ = cls.__bytes_cast
  106. @classmethod
  107. def __promise__(cls, klass, funcname, method):
  108. # Builds a wrapper around some magic method and registers that
  109. # magic method for the given type and method name.
  110. def __wrapper__(self, *args, **kw):
  111. # Automatically triggers the evaluation of a lazy value and
  112. # applies the given magic method of the result type.
  113. res = func(*self.__args, **self.__kw)
  114. for t in type(res).mro():
  115. if t in self.__dispatch:
  116. return self.__dispatch[t][funcname](res, *args, **kw)
  117. raise TypeError("Lazy object returned unexpected type.")
  118. if klass not in cls.__dispatch:
  119. cls.__dispatch[klass] = {}
  120. cls.__dispatch[klass][funcname] = method
  121. return __wrapper__
  122. def __text_cast(self):
  123. return func(*self.__args, **self.__kw)
  124. def __bytes_cast(self):
  125. return bytes(func(*self.__args, **self.__kw))
  126. def __cast(self):
  127. if self._delegate_bytes:
  128. return self.__bytes_cast()
  129. elif self._delegate_text:
  130. return self.__text_cast()
  131. else:
  132. return func(*self.__args, **self.__kw)
  133. def __ne__(self, other):
  134. if isinstance(other, Promise):
  135. other = other.__cast()
  136. return self.__cast() != other
  137. def __eq__(self, other):
  138. if isinstance(other, Promise):
  139. other = other.__cast()
  140. return self.__cast() == other
  141. def __lt__(self, other):
  142. if isinstance(other, Promise):
  143. other = other.__cast()
  144. return self.__cast() < other
  145. def __hash__(self):
  146. return hash(self.__cast())
  147. def __mod__(self, rhs):
  148. if self._delegate_bytes and six.PY2:
  149. return bytes(self) % rhs
  150. elif self._delegate_text:
  151. return six.text_type(self) % rhs
  152. return self.__cast() % rhs
  153. def __deepcopy__(self, memo):
  154. # Instances of this class are effectively immutable. It's just a
  155. # collection of functions. So we don't need to do anything
  156. # complicated for copying.
  157. memo[id(self)] = self
  158. return self
  159. @wraps(func)
  160. def __wrapper__(*args, **kw):
  161. # Creates the proxy object, instead of the actual value.
  162. return __proxy__(args, kw)
  163. return __wrapper__
  164. def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses):
  165. return lazy(func, *resultclasses)(*args, **kwargs)
  166. def allow_lazy(func, *resultclasses):
  167. """
  168. A decorator that allows a function to be called with one or more lazy
  169. arguments. If none of the args are lazy, the function is evaluated
  170. immediately, otherwise a __proxy__ is returned that will evaluate the
  171. function when needed.
  172. """
  173. @wraps(func)
  174. def wrapper(*args, **kwargs):
  175. for arg in list(args) + list(six.itervalues(kwargs)):
  176. if isinstance(arg, Promise):
  177. break
  178. else:
  179. return func(*args, **kwargs)
  180. return lazy(func, *resultclasses)(*args, **kwargs)
  181. return wrapper
  182. empty = object()
  183. def new_method_proxy(func):
  184. def inner(self, *args):
  185. if self._wrapped is empty:
  186. self._setup()
  187. return func(self._wrapped, *args)
  188. return inner
  189. class LazyObject(object):
  190. """
  191. A wrapper for another class that can be used to delay instantiation of the
  192. wrapped class.
  193. By subclassing, you have the opportunity to intercept and alter the
  194. instantiation. If you don't need to do that, use SimpleLazyObject.
  195. """
  196. # Avoid infinite recursion when tracing __init__ (#19456).
  197. _wrapped = None
  198. def __init__(self):
  199. self._wrapped = empty
  200. __getattr__ = new_method_proxy(getattr)
  201. def __setattr__(self, name, value):
  202. if name == "_wrapped":
  203. # Assign to __dict__ to avoid infinite __setattr__ loops.
  204. self.__dict__["_wrapped"] = value
  205. else:
  206. if self._wrapped is empty:
  207. self._setup()
  208. setattr(self._wrapped, name, value)
  209. def __delattr__(self, name):
  210. if name == "_wrapped":
  211. raise TypeError("can't delete _wrapped.")
  212. if self._wrapped is empty:
  213. self._setup()
  214. delattr(self._wrapped, name)
  215. def _setup(self):
  216. """
  217. Must be implemented by subclasses to initialize the wrapped object.
  218. """
  219. raise NotImplementedError('subclasses of LazyObject must provide a _setup() method')
  220. # Because we have messed with __class__ below, we confuse pickle as to what
  221. # class we are pickling. It also appears to stop __reduce__ from being
  222. # called. So, we define __getstate__ in a way that cooperates with the way
  223. # that pickle interprets this class. This fails when the wrapped class is
  224. # a builtin, but it is better than nothing.
  225. def __getstate__(self):
  226. if self._wrapped is empty:
  227. self._setup()
  228. return self._wrapped.__dict__
  229. # Python 3.3 will call __reduce__ when pickling; this method is needed
  230. # to serialize and deserialize correctly.
  231. @classmethod
  232. def __newobj__(cls, *args):
  233. return cls.__new__(cls, *args)
  234. def __reduce_ex__(self, proto):
  235. if proto >= 2:
  236. # On Py3, since the default protocol is 3, pickle uses the
  237. # ``__newobj__`` method (& more efficient opcodes) for writing.
  238. return (self.__newobj__, (self.__class__,), self.__getstate__())
  239. else:
  240. # On Py2, the default protocol is 0 (for back-compat) & the above
  241. # code fails miserably (see regression test). Instead, we return
  242. # exactly what's returned if there's no ``__reduce__`` method at
  243. # all.
  244. return (copyreg._reconstructor, (self.__class__, object, None), self.__getstate__())
  245. def __deepcopy__(self, memo):
  246. if self._wrapped is empty:
  247. # We have to use type(self), not self.__class__, because the
  248. # latter is proxied.
  249. result = type(self)()
  250. memo[id(self)] = result
  251. return result
  252. return copy.deepcopy(self._wrapped, memo)
  253. if six.PY3:
  254. __bytes__ = new_method_proxy(bytes)
  255. __str__ = new_method_proxy(str)
  256. __bool__ = new_method_proxy(bool)
  257. else:
  258. __str__ = new_method_proxy(str)
  259. __unicode__ = new_method_proxy(unicode)
  260. __nonzero__ = new_method_proxy(bool)
  261. # Introspection support
  262. __dir__ = new_method_proxy(dir)
  263. # Need to pretend to be the wrapped class, for the sake of objects that
  264. # care about this (especially in equality tests)
  265. __class__ = property(new_method_proxy(operator.attrgetter("__class__")))
  266. __eq__ = new_method_proxy(operator.eq)
  267. __ne__ = new_method_proxy(operator.ne)
  268. __hash__ = new_method_proxy(hash)
  269. # Dictionary methods support
  270. __getitem__ = new_method_proxy(operator.getitem)
  271. __setitem__ = new_method_proxy(operator.setitem)
  272. __delitem__ = new_method_proxy(operator.delitem)
  273. __len__ = new_method_proxy(len)
  274. __contains__ = new_method_proxy(operator.contains)
  275. # Workaround for http://bugs.python.org/issue12370
  276. _super = super
  277. class SimpleLazyObject(LazyObject):
  278. """
  279. A lazy object initialized from any function.
  280. Designed for compound objects of unknown type. For builtins or objects of
  281. known type, use django.utils.functional.lazy.
  282. """
  283. def __init__(self, func):
  284. """
  285. Pass in a callable that returns the object to be wrapped.
  286. If copies are made of the resulting SimpleLazyObject, which can happen
  287. in various circumstances within Django, then you must ensure that the
  288. callable can be safely run more than once and will return the same
  289. value.
  290. """
  291. self.__dict__['_setupfunc'] = func
  292. _super(SimpleLazyObject, self).__init__()
  293. def _setup(self):
  294. self._wrapped = self._setupfunc()
  295. # Return a meaningful representation of the lazy object for debugging
  296. # without evaluating the wrapped object.
  297. def __repr__(self):
  298. if self._wrapped is empty:
  299. repr_attr = self._setupfunc
  300. else:
  301. repr_attr = self._wrapped
  302. return '<%s: %r>' % (type(self).__name__, repr_attr)
  303. def __deepcopy__(self, memo):
  304. if self._wrapped is empty:
  305. # We have to use SimpleLazyObject, not self.__class__, because the
  306. # latter is proxied.
  307. result = SimpleLazyObject(self._setupfunc)
  308. memo[id(self)] = result
  309. return result
  310. return copy.deepcopy(self._wrapped, memo)
  311. class lazy_property(property):
  312. """
  313. A property that works with subclasses by wrapping the decorated
  314. functions of the base class.
  315. """
  316. def __new__(cls, fget=None, fset=None, fdel=None, doc=None):
  317. if fget is not None:
  318. @wraps(fget)
  319. def fget(instance, instance_type=None, name=fget.__name__):
  320. return getattr(instance, name)()
  321. if fset is not None:
  322. @wraps(fset)
  323. def fset(instance, value, name=fset.__name__):
  324. return getattr(instance, name)(value)
  325. if fdel is not None:
  326. @wraps(fdel)
  327. def fdel(instance, name=fdel.__name__):
  328. return getattr(instance, name)()
  329. return property(fget, fset, fdel, doc)
  330. def partition(predicate, values):
  331. """
  332. Splits the values into two sets, based on the return value of the function
  333. (True/False). e.g.:
  334. >>> partition(lambda x: x > 3, range(5))
  335. [0, 1, 2, 3], [4]
  336. """
  337. results = ([], [])
  338. for item in values:
  339. results[predicate(item)].append(item)
  340. return results
  341. if sys.version_info >= (2, 7, 2):
  342. from functools import total_ordering
  343. else:
  344. # For Python < 2.7.2. total_ordering in versions prior to 2.7.2 is buggy.
  345. # See http://bugs.python.org/issue10042 for details. For these versions use
  346. # code borrowed from Python 2.7.3.
  347. def total_ordering(cls):
  348. """Class decorator that fills in missing ordering methods"""
  349. convert = {
  350. '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)),
  351. ('__le__', lambda self, other: self < other or self == other),
  352. ('__ge__', lambda self, other: not self < other)],
  353. '__le__': [('__ge__', lambda self, other: not self <= other or self == other),
  354. ('__lt__', lambda self, other: self <= other and not self == other),
  355. ('__gt__', lambda self, other: not self <= other)],
  356. '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)),
  357. ('__ge__', lambda self, other: self > other or self == other),
  358. ('__le__', lambda self, other: not self > other)],
  359. '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other),
  360. ('__gt__', lambda self, other: self >= other and not self == other),
  361. ('__lt__', lambda self, other: not self >= other)]
  362. }
  363. roots = set(dir(cls)) & set(convert)
  364. if not roots:
  365. raise ValueError('must define at least one ordering operation: < > <= >=')
  366. root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
  367. for opname, opfunc in convert[root]:
  368. if opname not in roots:
  369. opfunc.__name__ = opname
  370. opfunc.__doc__ = getattr(int, opname).__doc__
  371. setattr(cls, opname, opfunc)
  372. return cls