functional.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. import copy
  2. import itertools
  3. import operator
  4. from functools import wraps
  5. class cached_property:
  6. """
  7. Decorator that converts a method with a single self argument into a
  8. property cached on the instance.
  9. A cached property can be made out of an existing method:
  10. (e.g. ``url = cached_property(get_absolute_url)``).
  11. """
  12. name = None
  13. @staticmethod
  14. def func(instance):
  15. raise TypeError(
  16. "Cannot use cached_property instance without calling "
  17. "__set_name__() on it."
  18. )
  19. def __init__(self, func):
  20. self.real_func = func
  21. self.__doc__ = getattr(func, "__doc__")
  22. def __set_name__(self, owner, name):
  23. if self.name is None:
  24. self.name = name
  25. self.func = self.real_func
  26. elif name != self.name:
  27. raise TypeError(
  28. "Cannot assign the same cached_property to two different names "
  29. "(%r and %r)." % (self.name, name)
  30. )
  31. def __get__(self, instance, cls=None):
  32. """
  33. Call the function and put the return value in instance.__dict__ so that
  34. subsequent attribute access on the instance returns the cached value
  35. instead of calling cached_property.__get__().
  36. """
  37. if instance is None:
  38. return self
  39. res = instance.__dict__[self.name] = self.func(instance)
  40. return res
  41. class classproperty:
  42. """
  43. Decorator that converts a method with a single cls argument into a property
  44. that can be accessed directly from the class.
  45. """
  46. def __init__(self, method=None):
  47. self.fget = method
  48. def __get__(self, instance, cls=None):
  49. return self.fget(cls)
  50. def getter(self, method):
  51. self.fget = method
  52. return self
  53. class Promise:
  54. """
  55. Base class for the proxy class created in the closure of the lazy function.
  56. It's used to recognize promises in code.
  57. """
  58. pass
  59. def lazy(func, *resultclasses):
  60. """
  61. Turn any callable into a lazy evaluated callable. result classes or types
  62. is required -- at least one is needed so that the automatic forcing of
  63. the lazy evaluation code is triggered. Results are not memoized; the
  64. function is evaluated on every access.
  65. """
  66. class __proxy__(Promise):
  67. """
  68. Encapsulate a function call and act as a proxy for methods that are
  69. called on the result of that function. The function is not evaluated
  70. until one of the methods on the result is called.
  71. """
  72. def __init__(self, args, kw):
  73. self._args = args
  74. self._kw = kw
  75. def __reduce__(self):
  76. return (
  77. _lazy_proxy_unpickle,
  78. (func, self._args, self._kw) + resultclasses,
  79. )
  80. def __deepcopy__(self, memo):
  81. # Instances of this class are effectively immutable. It's just a
  82. # collection of functions. So we don't need to do anything
  83. # complicated for copying.
  84. memo[id(self)] = self
  85. return self
  86. def __cast(self):
  87. return func(*self._args, **self._kw)
  88. # Explicitly wrap methods which are defined on object and hence would
  89. # not have been overloaded by the loop over resultclasses below.
  90. def __repr__(self):
  91. return repr(self.__cast())
  92. def __str__(self):
  93. return str(self.__cast())
  94. def __eq__(self, other):
  95. if isinstance(other, Promise):
  96. other = other.__cast()
  97. return self.__cast() == other
  98. def __ne__(self, other):
  99. if isinstance(other, Promise):
  100. other = other.__cast()
  101. return self.__cast() != other
  102. def __lt__(self, other):
  103. if isinstance(other, Promise):
  104. other = other.__cast()
  105. return self.__cast() < other
  106. def __le__(self, other):
  107. if isinstance(other, Promise):
  108. other = other.__cast()
  109. return self.__cast() <= other
  110. def __gt__(self, other):
  111. if isinstance(other, Promise):
  112. other = other.__cast()
  113. return self.__cast() > other
  114. def __ge__(self, other):
  115. if isinstance(other, Promise):
  116. other = other.__cast()
  117. return self.__cast() >= other
  118. def __hash__(self):
  119. return hash(self.__cast())
  120. def __format__(self, format_spec):
  121. return format(self.__cast(), format_spec)
  122. # Explicitly wrap methods which are required for certain operations on
  123. # int/str objects to function correctly.
  124. def __add__(self, other):
  125. return self.__cast() + other
  126. def __radd__(self, other):
  127. return other + self.__cast()
  128. def __mod__(self, other):
  129. return self.__cast() % other
  130. def __mul__(self, other):
  131. return self.__cast() * other
  132. # Add wrappers for all methods from resultclasses which haven't been
  133. # wrapped explicitly above.
  134. for resultclass in resultclasses:
  135. for type_ in resultclass.mro():
  136. for method_name in type_.__dict__:
  137. # All __promise__ return the same wrapper method, they look up
  138. # the correct implementation when called.
  139. if hasattr(__proxy__, method_name):
  140. continue
  141. # Builds a wrapper around some method. Pass method_name to
  142. # avoid issues due to late binding.
  143. def __wrapper__(self, *args, __method_name=method_name, **kw):
  144. # Automatically triggers the evaluation of a lazy value and
  145. # applies the given method of the result type.
  146. result = func(*self._args, **self._kw)
  147. return getattr(result, __method_name)(*args, **kw)
  148. setattr(__proxy__, method_name, __wrapper__)
  149. @wraps(func)
  150. def __wrapper__(*args, **kw):
  151. # Creates the proxy object, instead of the actual value.
  152. return __proxy__(args, kw)
  153. return __wrapper__
  154. def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses):
  155. return lazy(func, *resultclasses)(*args, **kwargs)
  156. def lazystr(text):
  157. """
  158. Shortcut for the common case of a lazy callable that returns str.
  159. """
  160. return lazy(str, str)(text)
  161. def keep_lazy(*resultclasses):
  162. """
  163. A decorator that allows a function to be called with one or more lazy
  164. arguments. If none of the args are lazy, the function is evaluated
  165. immediately, otherwise a __proxy__ is returned that will evaluate the
  166. function when needed.
  167. """
  168. if not resultclasses:
  169. raise TypeError("You must pass at least one argument to keep_lazy().")
  170. def decorator(func):
  171. lazy_func = lazy(func, *resultclasses)
  172. @wraps(func)
  173. def wrapper(*args, **kwargs):
  174. if any(
  175. isinstance(arg, Promise)
  176. for arg in itertools.chain(args, kwargs.values())
  177. ):
  178. return lazy_func(*args, **kwargs)
  179. return func(*args, **kwargs)
  180. return wrapper
  181. return decorator
  182. def keep_lazy_text(func):
  183. """
  184. A decorator for functions that accept lazy arguments and return text.
  185. """
  186. return keep_lazy(str)(func)
  187. empty = object()
  188. def new_method_proxy(func):
  189. def inner(self, *args):
  190. if (_wrapped := self._wrapped) is empty:
  191. self._setup()
  192. _wrapped = self._wrapped
  193. return func(_wrapped, *args)
  194. inner._mask_wrapped = False
  195. return inner
  196. class LazyObject:
  197. """
  198. A wrapper for another class that can be used to delay instantiation of the
  199. wrapped class.
  200. By subclassing, you have the opportunity to intercept and alter the
  201. instantiation. If you don't need to do that, use SimpleLazyObject.
  202. """
  203. # Avoid infinite recursion when tracing __init__ (#19456).
  204. _wrapped = None
  205. def __init__(self):
  206. # Note: if a subclass overrides __init__(), it will likely need to
  207. # override __copy__() and __deepcopy__() as well.
  208. self._wrapped = empty
  209. def __getattribute__(self, name):
  210. if name == "_wrapped":
  211. # Avoid recursion when getting wrapped object.
  212. return super().__getattribute__(name)
  213. value = super().__getattribute__(name)
  214. # If attribute is a proxy method, raise an AttributeError to call
  215. # __getattr__() and use the wrapped object method.
  216. if not getattr(value, "_mask_wrapped", True):
  217. raise AttributeError
  218. return value
  219. __getattr__ = new_method_proxy(getattr)
  220. def __setattr__(self, name, value):
  221. if name == "_wrapped":
  222. # Assign to __dict__ to avoid infinite __setattr__ loops.
  223. self.__dict__["_wrapped"] = value
  224. else:
  225. if self._wrapped is empty:
  226. self._setup()
  227. setattr(self._wrapped, name, value)
  228. def __delattr__(self, name):
  229. if name == "_wrapped":
  230. raise TypeError("can't delete _wrapped.")
  231. if self._wrapped is empty:
  232. self._setup()
  233. delattr(self._wrapped, name)
  234. def _setup(self):
  235. """
  236. Must be implemented by subclasses to initialize the wrapped object.
  237. """
  238. raise NotImplementedError(
  239. "subclasses of LazyObject must provide a _setup() method"
  240. )
  241. # Because we have messed with __class__ below, we confuse pickle as to what
  242. # class we are pickling. We're going to have to initialize the wrapped
  243. # object to successfully pickle it, so we might as well just pickle the
  244. # wrapped object since they're supposed to act the same way.
  245. #
  246. # Unfortunately, if we try to simply act like the wrapped object, the ruse
  247. # will break down when pickle gets our id(). Thus we end up with pickle
  248. # thinking, in effect, that we are a distinct object from the wrapped
  249. # object, but with the same __dict__. This can cause problems (see #25389).
  250. #
  251. # So instead, we define our own __reduce__ method and custom unpickler. We
  252. # pickle the wrapped object as the unpickler's argument, so that pickle
  253. # will pickle it normally, and then the unpickler simply returns its
  254. # argument.
  255. def __reduce__(self):
  256. if self._wrapped is empty:
  257. self._setup()
  258. return (unpickle_lazyobject, (self._wrapped,))
  259. def __copy__(self):
  260. if self._wrapped is empty:
  261. # If uninitialized, copy the wrapper. Use type(self), not
  262. # self.__class__, because the latter is proxied.
  263. return type(self)()
  264. else:
  265. # If initialized, return a copy of the wrapped object.
  266. return copy.copy(self._wrapped)
  267. def __deepcopy__(self, memo):
  268. if self._wrapped is empty:
  269. # We have to use type(self), not self.__class__, because the
  270. # latter is proxied.
  271. result = type(self)()
  272. memo[id(self)] = result
  273. return result
  274. return copy.deepcopy(self._wrapped, memo)
  275. __bytes__ = new_method_proxy(bytes)
  276. __str__ = new_method_proxy(str)
  277. __bool__ = new_method_proxy(bool)
  278. # Introspection support
  279. __dir__ = new_method_proxy(dir)
  280. # Need to pretend to be the wrapped class, for the sake of objects that
  281. # care about this (especially in equality tests)
  282. __class__ = property(new_method_proxy(operator.attrgetter("__class__")))
  283. __eq__ = new_method_proxy(operator.eq)
  284. __lt__ = new_method_proxy(operator.lt)
  285. __gt__ = new_method_proxy(operator.gt)
  286. __ne__ = new_method_proxy(operator.ne)
  287. __hash__ = new_method_proxy(hash)
  288. # List/Tuple/Dictionary methods support
  289. __getitem__ = new_method_proxy(operator.getitem)
  290. __setitem__ = new_method_proxy(operator.setitem)
  291. __delitem__ = new_method_proxy(operator.delitem)
  292. __iter__ = new_method_proxy(iter)
  293. __len__ = new_method_proxy(len)
  294. __contains__ = new_method_proxy(operator.contains)
  295. def unpickle_lazyobject(wrapped):
  296. """
  297. Used to unpickle lazy objects. Just return its argument, which will be the
  298. wrapped object.
  299. """
  300. return wrapped
  301. class SimpleLazyObject(LazyObject):
  302. """
  303. A lazy object initialized from any function.
  304. Designed for compound objects of unknown type. For builtins or objects of
  305. known type, use django.utils.functional.lazy.
  306. """
  307. def __init__(self, func):
  308. """
  309. Pass in a callable that returns the object to be wrapped.
  310. If copies are made of the resulting SimpleLazyObject, which can happen
  311. in various circumstances within Django, then you must ensure that the
  312. callable can be safely run more than once and will return the same
  313. value.
  314. """
  315. self.__dict__["_setupfunc"] = func
  316. super().__init__()
  317. def _setup(self):
  318. self._wrapped = self._setupfunc()
  319. # Return a meaningful representation of the lazy object for debugging
  320. # without evaluating the wrapped object.
  321. def __repr__(self):
  322. if self._wrapped is empty:
  323. repr_attr = self._setupfunc
  324. else:
  325. repr_attr = self._wrapped
  326. return "<%s: %r>" % (type(self).__name__, repr_attr)
  327. def __copy__(self):
  328. if self._wrapped is empty:
  329. # If uninitialized, copy the wrapper. Use SimpleLazyObject, not
  330. # self.__class__, because the latter is proxied.
  331. return SimpleLazyObject(self._setupfunc)
  332. else:
  333. # If initialized, return a copy of the wrapped object.
  334. return copy.copy(self._wrapped)
  335. def __deepcopy__(self, memo):
  336. if self._wrapped is empty:
  337. # We have to use SimpleLazyObject, not self.__class__, because the
  338. # latter is proxied.
  339. result = SimpleLazyObject(self._setupfunc)
  340. memo[id(self)] = result
  341. return result
  342. return copy.deepcopy(self._wrapped, memo)
  343. __add__ = new_method_proxy(operator.add)
  344. @new_method_proxy
  345. def __radd__(self, other):
  346. return other + self
  347. def partition(predicate, values):
  348. """
  349. Split the values into two sets, based on the return value of the function
  350. (True/False). e.g.:
  351. >>> partition(lambda x: x > 3, range(5))
  352. [0, 1, 2, 3], [4]
  353. """
  354. results = ([], [])
  355. for item in values:
  356. results[predicate(item)].append(item)
  357. return results