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.
 
 
 
 

414 lines
14 KiB

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