You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

functional.py 15KB

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