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