Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/django/utils/functional.py: 32%
228 statements
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
1import copy
2import itertools
3import operator
4import warnings
5from functools import total_ordering, wraps
8class cached_property:
9 """
10 Decorator that converts a method with a single self argument into a
11 property cached on the instance.
13 A cached property can be made out of an existing method:
14 (e.g. ``url = cached_property(get_absolute_url)``).
15 """
17 name = None
19 @staticmethod
20 def func(instance):
21 raise TypeError(
22 "Cannot use cached_property instance without calling "
23 "__set_name__() on it."
24 )
26 def __init__(self, func, name=None):
27 from django.utils.deprecation import RemovedInDjango50Warning
29 if name is not None:
30 warnings.warn(
31 "The name argument is deprecated as it's unnecessary as of "
32 "Python 3.6.",
33 RemovedInDjango50Warning,
34 stacklevel=2,
35 )
36 self.real_func = func
37 self.__doc__ = getattr(func, "__doc__")
39 def __set_name__(self, owner, name):
40 if self.name is None:
41 self.name = name
42 self.func = self.real_func
43 elif name != self.name:
44 raise TypeError(
45 "Cannot assign the same cached_property to two different names "
46 "(%r and %r)." % (self.name, name)
47 )
49 def __get__(self, instance, cls=None):
50 """
51 Call the function and put the return value in instance.__dict__ so that
52 subsequent attribute access on the instance returns the cached value
53 instead of calling cached_property.__get__().
54 """
55 if instance is None:
56 return self
57 res = instance.__dict__[self.name] = self.func(instance)
58 return res
61class classproperty:
62 """
63 Decorator that converts a method with a single cls argument into a property
64 that can be accessed directly from the class.
65 """
67 def __init__(self, method=None):
68 self.fget = method
70 def __get__(self, instance, cls=None):
71 return self.fget(cls)
73 def getter(self, method):
74 self.fget = method
75 return self
78class Promise:
79 """
80 Base class for the proxy class created in the closure of the lazy function.
81 It's used to recognize promises in code.
82 """
84 pass
87def lazy(func, *resultclasses):
88 """
89 Turn any callable into a lazy evaluated callable. result classes or types
90 is required -- at least one is needed so that the automatic forcing of
91 the lazy evaluation code is triggered. Results are not memoized; the
92 function is evaluated on every access.
93 """
95 @total_ordering
96 class __proxy__(Promise):
97 """
98 Encapsulate a function call and act as a proxy for methods that are
99 called on the result of that function. The function is not evaluated
100 until one of the methods on the result is called.
101 """
103 __prepared = False
105 def __init__(self, args, kw):
106 self.__args = args
107 self.__kw = kw
108 if not self.__prepared:
109 self.__prepare_class__()
110 self.__class__.__prepared = True
112 def __reduce__(self):
113 return (
114 _lazy_proxy_unpickle,
115 (func, self.__args, self.__kw) + resultclasses,
116 )
118 def __repr__(self):
119 return repr(self.__cast())
121 @classmethod
122 def __prepare_class__(cls):
123 for resultclass in resultclasses:
124 for type_ in resultclass.mro():
125 for method_name in type_.__dict__:
126 # All __promise__ return the same wrapper method, they
127 # look up the correct implementation when called.
128 if hasattr(cls, method_name):
129 continue
130 meth = cls.__promise__(method_name)
131 setattr(cls, method_name, meth)
132 cls._delegate_bytes = bytes in resultclasses
133 cls._delegate_text = str in resultclasses
134 if cls._delegate_bytes and cls._delegate_text:
135 raise ValueError(
136 "Cannot call lazy() with both bytes and text return types."
137 )
138 if cls._delegate_text:
139 cls.__str__ = cls.__text_cast
140 elif cls._delegate_bytes:
141 cls.__bytes__ = cls.__bytes_cast
143 @classmethod
144 def __promise__(cls, method_name):
145 # Builds a wrapper around some magic method
146 def __wrapper__(self, *args, **kw):
147 # Automatically triggers the evaluation of a lazy value and
148 # applies the given magic method of the result type.
149 res = func(*self.__args, **self.__kw)
150 return getattr(res, method_name)(*args, **kw)
152 return __wrapper__
154 def __text_cast(self):
155 return func(*self.__args, **self.__kw)
157 def __bytes_cast(self):
158 return bytes(func(*self.__args, **self.__kw))
160 def __bytes_cast_encoded(self):
161 return func(*self.__args, **self.__kw).encode()
163 def __cast(self):
164 if self._delegate_bytes:
165 return self.__bytes_cast()
166 elif self._delegate_text:
167 return self.__text_cast()
168 else:
169 return func(*self.__args, **self.__kw)
171 def __str__(self):
172 # object defines __str__(), so __prepare_class__() won't overload
173 # a __str__() method from the proxied class.
174 return str(self.__cast())
176 def __eq__(self, other):
177 if isinstance(other, Promise):
178 other = other.__cast()
179 return self.__cast() == other
181 def __lt__(self, other):
182 if isinstance(other, Promise):
183 other = other.__cast()
184 return self.__cast() < other
186 def __hash__(self):
187 return hash(self.__cast())
189 def __mod__(self, rhs):
190 if self._delegate_text:
191 return str(self) % rhs
192 return self.__cast() % rhs
194 def __add__(self, other):
195 return self.__cast() + other
197 def __radd__(self, other):
198 return other + self.__cast()
200 def __deepcopy__(self, memo):
201 # Instances of this class are effectively immutable. It's just a
202 # collection of functions. So we don't need to do anything
203 # complicated for copying.
204 memo[id(self)] = self
205 return self
207 @wraps(func)
208 def __wrapper__(*args, **kw):
209 # Creates the proxy object, instead of the actual value.
210 return __proxy__(args, kw)
212 return __wrapper__
215def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses):
216 return lazy(func, *resultclasses)(*args, **kwargs)
219def lazystr(text):
220 """
221 Shortcut for the common case of a lazy callable that returns str.
222 """
223 return lazy(str, str)(text)
226def keep_lazy(*resultclasses):
227 """
228 A decorator that allows a function to be called with one or more lazy
229 arguments. If none of the args are lazy, the function is evaluated
230 immediately, otherwise a __proxy__ is returned that will evaluate the
231 function when needed.
232 """
233 if not resultclasses:
234 raise TypeError("You must pass at least one argument to keep_lazy().")
236 def decorator(func):
237 lazy_func = lazy(func, *resultclasses)
239 @wraps(func)
240 def wrapper(*args, **kwargs):
241 if any(
242 isinstance(arg, Promise)
243 for arg in itertools.chain(args, kwargs.values())
244 ):
245 return lazy_func(*args, **kwargs)
246 return func(*args, **kwargs)
248 return wrapper
250 return decorator
253def keep_lazy_text(func):
254 """
255 A decorator for functions that accept lazy arguments and return text.
256 """
257 return keep_lazy(str)(func)
260empty = object()
263def new_method_proxy(func):
264 def inner(self, *args):
265 if (_wrapped := self._wrapped) is empty:
266 self._setup()
267 _wrapped = self._wrapped
268 return func(_wrapped, *args)
270 inner._mask_wrapped = False
271 return inner
274class LazyObject:
275 """
276 A wrapper for another class that can be used to delay instantiation of the
277 wrapped class.
279 By subclassing, you have the opportunity to intercept and alter the
280 instantiation. If you don't need to do that, use SimpleLazyObject.
281 """
283 # Avoid infinite recursion when tracing __init__ (#19456).
284 _wrapped = None
286 def __init__(self):
287 # Note: if a subclass overrides __init__(), it will likely need to
288 # override __copy__() and __deepcopy__() as well.
289 self._wrapped = empty
291 def __getattribute__(self, name):
292 if name == "_wrapped":
293 # Avoid recursion when getting wrapped object.
294 return super().__getattribute__(name)
295 value = super().__getattribute__(name)
296 # If attribute is a proxy method, raise an AttributeError to call
297 # __getattr__() and use the wrapped object method.
298 if not getattr(value, "_mask_wrapped", True):
299 raise AttributeError
300 return value
302 __getattr__ = new_method_proxy(getattr)
304 def __setattr__(self, name, value):
305 if name == "_wrapped":
306 # Assign to __dict__ to avoid infinite __setattr__ loops.
307 self.__dict__["_wrapped"] = value
308 else:
309 if self._wrapped is empty:
310 self._setup()
311 setattr(self._wrapped, name, value)
313 def __delattr__(self, name):
314 if name == "_wrapped":
315 raise TypeError("can't delete _wrapped.")
316 if self._wrapped is empty:
317 self._setup()
318 delattr(self._wrapped, name)
320 def _setup(self):
321 """
322 Must be implemented by subclasses to initialize the wrapped object.
323 """
324 raise NotImplementedError(
325 "subclasses of LazyObject must provide a _setup() method"
326 )
328 # Because we have messed with __class__ below, we confuse pickle as to what
329 # class we are pickling. We're going to have to initialize the wrapped
330 # object to successfully pickle it, so we might as well just pickle the
331 # wrapped object since they're supposed to act the same way.
332 #
333 # Unfortunately, if we try to simply act like the wrapped object, the ruse
334 # will break down when pickle gets our id(). Thus we end up with pickle
335 # thinking, in effect, that we are a distinct object from the wrapped
336 # object, but with the same __dict__. This can cause problems (see #25389).
337 #
338 # So instead, we define our own __reduce__ method and custom unpickler. We
339 # pickle the wrapped object as the unpickler's argument, so that pickle
340 # will pickle it normally, and then the unpickler simply returns its
341 # argument.
342 def __reduce__(self):
343 if self._wrapped is empty:
344 self._setup()
345 return (unpickle_lazyobject, (self._wrapped,))
347 def __copy__(self):
348 if self._wrapped is empty:
349 # If uninitialized, copy the wrapper. Use type(self), not
350 # self.__class__, because the latter is proxied.
351 return type(self)()
352 else:
353 # If initialized, return a copy of the wrapped object.
354 return copy.copy(self._wrapped)
356 def __deepcopy__(self, memo):
357 if self._wrapped is empty:
358 # We have to use type(self), not self.__class__, because the
359 # latter is proxied.
360 result = type(self)()
361 memo[id(self)] = result
362 return result
363 return copy.deepcopy(self._wrapped, memo)
365 __bytes__ = new_method_proxy(bytes)
366 __str__ = new_method_proxy(str)
367 __bool__ = new_method_proxy(bool)
369 # Introspection support
370 __dir__ = new_method_proxy(dir)
372 # Need to pretend to be the wrapped class, for the sake of objects that
373 # care about this (especially in equality tests)
374 __class__ = property(new_method_proxy(operator.attrgetter("__class__")))
375 __eq__ = new_method_proxy(operator.eq)
376 __lt__ = new_method_proxy(operator.lt)
377 __gt__ = new_method_proxy(operator.gt)
378 __ne__ = new_method_proxy(operator.ne)
379 __hash__ = new_method_proxy(hash)
381 # List/Tuple/Dictionary methods support
382 __getitem__ = new_method_proxy(operator.getitem)
383 __setitem__ = new_method_proxy(operator.setitem)
384 __delitem__ = new_method_proxy(operator.delitem)
385 __iter__ = new_method_proxy(iter)
386 __len__ = new_method_proxy(len)
387 __contains__ = new_method_proxy(operator.contains)
390def unpickle_lazyobject(wrapped):
391 """
392 Used to unpickle lazy objects. Just return its argument, which will be the
393 wrapped object.
394 """
395 return wrapped
398class SimpleLazyObject(LazyObject):
399 """
400 A lazy object initialized from any function.
402 Designed for compound objects of unknown type. For builtins or objects of
403 known type, use django.utils.functional.lazy.
404 """
406 def __init__(self, func):
407 """
408 Pass in a callable that returns the object to be wrapped.
410 If copies are made of the resulting SimpleLazyObject, which can happen
411 in various circumstances within Django, then you must ensure that the
412 callable can be safely run more than once and will return the same
413 value.
414 """
415 self.__dict__["_setupfunc"] = func
416 super().__init__()
418 def _setup(self):
419 self._wrapped = self._setupfunc()
421 # Return a meaningful representation of the lazy object for debugging
422 # without evaluating the wrapped object.
423 def __repr__(self):
424 if self._wrapped is empty:
425 repr_attr = self._setupfunc
426 else:
427 repr_attr = self._wrapped
428 return "<%s: %r>" % (type(self).__name__, repr_attr)
430 def __copy__(self):
431 if self._wrapped is empty:
432 # If uninitialized, copy the wrapper. Use SimpleLazyObject, not
433 # self.__class__, because the latter is proxied.
434 return SimpleLazyObject(self._setupfunc)
435 else:
436 # If initialized, return a copy of the wrapped object.
437 return copy.copy(self._wrapped)
439 def __deepcopy__(self, memo):
440 if self._wrapped is empty:
441 # We have to use SimpleLazyObject, not self.__class__, because the
442 # latter is proxied.
443 result = SimpleLazyObject(self._setupfunc)
444 memo[id(self)] = result
445 return result
446 return copy.deepcopy(self._wrapped, memo)
448 __add__ = new_method_proxy(operator.add)
450 @new_method_proxy
451 def __radd__(self, other):
452 return other + self
455def partition(predicate, values):
456 """
457 Split the values into two sets, based on the return value of the function
458 (True/False). e.g.:
460 >>> partition(lambda x: x > 3, range(5))
461 [0, 1, 2, 3], [4]
462 """
463 results = ([], [])
464 for item in values:
465 results[predicate(item)].append(item)
466 return results