• fr
  • Language: en

Source code for django.utils.functional

import copy
import operator
import sys
import warnings
from functools import wraps

from django.utils import six
from django.utils.deprecation import RemovedInDjango19Warning


# You can't trivially replace this with `functools.partial` because this binds
# to classes and returns bound instances, whereas functools.partial (on
# CPython) is a type and its instances don't bind.
def curry(_curried_func, *args, **kwargs):
    def _curried(*moreargs, **morekwargs):
        return _curried_func(*(args + moreargs), **dict(kwargs, **morekwargs))
    return _curried


def memoize(func, cache, num_args):
    """
    Wrap a function so that results for any argument tuple are stored in
    'cache'. Note that the args to the function must be usable as dictionary
    keys.

    Only the first num_args are considered when creating the key.
    """
    warnings.warn("memoize wrapper is deprecated and will be removed in "
                  "Django 1.9. Use django.utils.lru_cache instead.",
                  RemovedInDjango19Warning, stacklevel=2)

    @wraps(func)
    def wrapper(*args):
        mem_args = args[:num_args]
        if mem_args in cache:
            return cache[mem_args]
        result = func(*args)
        cache[mem_args] = result
        return result
    return wrapper


[docs]class cached_property(object): """ Decorator that converts a method with a single self argument into a property cached on the instance. Optional ``name`` argument allows you to make cached properties of other methods. (e.g. url = cached_property(get_absolute_url, name='url') ) """ def __init__(self, func, name=None): self.func = func self.__doc__ = getattr(func, '__doc__') self.name = name or func.__name__ def __get__(self, instance, type=None): if instance is None: return self res = instance.__dict__[self.name] = self.func(instance) return res
class Promise(object): """ This is just a base class for the proxy class created in the closure of the lazy function. It can be used to recognize promises in code. """ pass def lazy(func, *resultclasses): """ Turns any callable into a lazy evaluated callable. You need to give result classes or types -- at least one is needed so that the automatic forcing of the lazy evaluation code is triggered. Results are not memoized; the function is evaluated on every access. """ @total_ordering class __proxy__(Promise): """ Encapsulate a function call and act as a proxy for methods that are called on the result of that function. The function is not evaluated until one of the methods on the result is called. """ __prepared = False def __init__(self, args, kw): self.__args = args self.__kw = kw if not self.__prepared: self.__prepare_class__() self.__prepared = True def __reduce__(self): return ( _lazy_proxy_unpickle, (func, self.__args, self.__kw) + resultclasses ) @classmethod def __prepare_class__(cls): for resultclass in resultclasses: for type_ in resultclass.mro(): for method_name in type_.__dict__.keys(): # All __promise__ return the same wrapper method, they # look up the correct implementation when called. if hasattr(cls, method_name): continue meth = cls.__promise__(method_name) setattr(cls, method_name, meth) cls._delegate_bytes = bytes in resultclasses cls._delegate_text = six.text_type in resultclasses assert not (cls._delegate_bytes and cls._delegate_text), ( "Cannot call lazy() with both bytes and text return types.") if cls._delegate_text: if six.PY3: cls.__str__ = cls.__text_cast else: cls.__unicode__ = cls.__text_cast cls.__str__ = cls.__bytes_cast_encoded elif cls._delegate_bytes: if six.PY3: cls.__bytes__ = cls.__bytes_cast else: cls.__str__ = cls.__bytes_cast @classmethod def __promise__(cls, method_name): # Builds a wrapper around some magic method def __wrapper__(self, *args, **kw): # Automatically triggers the evaluation of a lazy value and # applies the given magic method of the result type. res = func(*self.__args, **self.__kw) return getattr(res, method_name)(*args, **kw) return __wrapper__ def __text_cast(self): return func(*self.__args, **self.__kw) def __bytes_cast(self): return bytes(func(*self.__args, **self.__kw)) def __bytes_cast_encoded(self): return func(*self.__args, **self.__kw).encode('utf-8') def __cast(self): if self._delegate_bytes: return self.__bytes_cast() elif self._delegate_text: return self.__text_cast() else: return func(*self.__args, **self.__kw) def __ne__(self, other): if isinstance(other, Promise): other = other.__cast() return self.__cast() != other def __eq__(self, other): if isinstance(other, Promise): other = other.__cast() return self.__cast() == other def __lt__(self, other): if isinstance(other, Promise): other = other.__cast() return self.__cast() < other def __hash__(self): return hash(self.__cast()) def __mod__(self, rhs): if self._delegate_bytes and six.PY2: return bytes(self) % rhs elif self._delegate_text: return six.text_type(self) % rhs return self.__cast() % rhs def __deepcopy__(self, memo): # Instances of this class are effectively immutable. It's just a # collection of functions. So we don't need to do anything # complicated for copying. memo[id(self)] = self return self @wraps(func) def __wrapper__(*args, **kw): # Creates the proxy object, instead of the actual value. return __proxy__(args, kw) return __wrapper__ def _lazy_proxy_unpickle(func, args, kwargs, *resultclasses): return lazy(func, *resultclasses)(*args, **kwargs)
[docs]def allow_lazy(func, *resultclasses): """ A decorator that allows a function to be called with one or more lazy arguments. If none of the args are lazy, the function is evaluated immediately, otherwise a __proxy__ is returned that will evaluate the function when needed. """ lazy_func = lazy(func, *resultclasses) @wraps(func) def wrapper(*args, **kwargs): for arg in list(args) + list(six.itervalues(kwargs)): if isinstance(arg, Promise): break else: return func(*args, **kwargs) return lazy_func(*args, **kwargs) return wrapper
empty = object() def new_method_proxy(func): def inner(self, *args): if self._wrapped is empty: self._setup() return func(self._wrapped, *args) return inner class LazyObject(object): """ A wrapper for another class that can be used to delay instantiation of the wrapped class. By subclassing, you have the opportunity to intercept and alter the instantiation. If you don't need to do that, use SimpleLazyObject. """ # Avoid infinite recursion when tracing __init__ (#19456). _wrapped = None def __init__(self): # Note: if a subclass overrides __init__(), it will likely need to # override __copy__() and __deepcopy__() as well. self._wrapped = empty __getattr__ = new_method_proxy(getattr) def __setattr__(self, name, value): if name == "_wrapped": # Assign to __dict__ to avoid infinite __setattr__ loops. self.__dict__["_wrapped"] = value else: if self._wrapped is empty: self._setup() setattr(self._wrapped, name, value) def __delattr__(self, name): if name == "_wrapped": raise TypeError("can't delete _wrapped.") if self._wrapped is empty: self._setup() delattr(self._wrapped, name) def _setup(self): """ Must be implemented by subclasses to initialize the wrapped object. """ raise NotImplementedError('subclasses of LazyObject must provide a _setup() method') # Because we have messed with __class__ below, we confuse pickle as to what # class we are pickling. We're going to have to initialize the wrapped # object to successfully pickle it, so we might as well just pickle the # wrapped object since they're supposed to act the same way. # # Unfortunately, if we try to simply act like the wrapped object, the ruse # will break down when pickle gets our id(). Thus we end up with pickle # thinking, in effect, that we are a distinct object from the wrapped # object, but with the same __dict__. This can cause problems (see #25389). # # So instead, we define our own __reduce__ method and custom unpickler. We # pickle the wrapped object as the unpickler's argument, so that pickle # will pickle it normally, and then the unpickler simply returns its # argument. def __reduce__(self): if self._wrapped is empty: self._setup() return (unpickle_lazyobject, (self._wrapped,)) # We have to explicitly override __getstate__ so that older versions of # pickle don't try to pickle the __dict__ (which in the case of a # SimpleLazyObject may contain a lambda). The value will end up being # ignored by our __reduce__ and custom unpickler. def __getstate__(self): return {} def __copy__(self): if self._wrapped is empty: # If uninitialized, copy the wrapper. Use type(self), not # self.__class__, because the latter is proxied. return type(self)() else: # If initialized, return a copy of the wrapped object. return copy.copy(self._wrapped) def __deepcopy__(self, memo): if self._wrapped is empty: # We have to use type(self), not self.__class__, because the # latter is proxied. result = type(self)() memo[id(self)] = result return result return copy.deepcopy(self._wrapped, memo) if six.PY3: __bytes__ = new_method_proxy(bytes) __str__ = new_method_proxy(str) __bool__ = new_method_proxy(bool) else: __str__ = new_method_proxy(str) __unicode__ = new_method_proxy(unicode) # NOQA: unicode undefined on PY3 __nonzero__ = new_method_proxy(bool) # Introspection support __dir__ = new_method_proxy(dir) # Need to pretend to be the wrapped class, for the sake of objects that # care about this (especially in equality tests) __class__ = property(new_method_proxy(operator.attrgetter("__class__"))) __eq__ = new_method_proxy(operator.eq) __ne__ = new_method_proxy(operator.ne) __hash__ = new_method_proxy(hash) # Dictionary methods support __getitem__ = new_method_proxy(operator.getitem) __setitem__ = new_method_proxy(operator.setitem) __delitem__ = new_method_proxy(operator.delitem) __len__ = new_method_proxy(len) __contains__ = new_method_proxy(operator.contains) def unpickle_lazyobject(wrapped): """ Used to unpickle lazy objects. Just return its argument, which will be the wrapped object. """ return wrapped unpickle_lazyobject.__safe_for_unpickling__ = True # Workaround for http://bugs.python.org/issue12370 _super = super class SimpleLazyObject(LazyObject): """ A lazy object initialized from any function. Designed for compound objects of unknown type. For builtins or objects of known type, use django.utils.functional.lazy. """ def __init__(self, func): """ Pass in a callable that returns the object to be wrapped. If copies are made of the resulting SimpleLazyObject, which can happen in various circumstances within Django, then you must ensure that the callable can be safely run more than once and will return the same value. """ self.__dict__['_setupfunc'] = func _super(SimpleLazyObject, self).__init__() def _setup(self): self._wrapped = self._setupfunc() # Return a meaningful representation of the lazy object for debugging # without evaluating the wrapped object. def __repr__(self): if self._wrapped is empty: repr_attr = self._setupfunc else: repr_attr = self._wrapped return '<%s: %r>' % (type(self).__name__, repr_attr) def __copy__(self): if self._wrapped is empty: # If uninitialized, copy the wrapper. Use SimpleLazyObject, not # self.__class__, because the latter is proxied. return SimpleLazyObject(self._setupfunc) else: # If initialized, return a copy of the wrapped object. return copy.copy(self._wrapped) def __deepcopy__(self, memo): if self._wrapped is empty: # We have to use SimpleLazyObject, not self.__class__, because the # latter is proxied. result = SimpleLazyObject(self._setupfunc) memo[id(self)] = result return result return copy.deepcopy(self._wrapped, memo) class lazy_property(property): """ A property that works with subclasses by wrapping the decorated functions of the base class. """ def __new__(cls, fget=None, fset=None, fdel=None, doc=None): if fget is not None: @wraps(fget) def fget(instance, instance_type=None, name=fget.__name__): return getattr(instance, name)() if fset is not None: @wraps(fset) def fset(instance, value, name=fset.__name__): return getattr(instance, name)(value) if fdel is not None: @wraps(fdel) def fdel(instance, name=fdel.__name__): return getattr(instance, name)() return property(fget, fset, fdel, doc) def partition(predicate, values): """ Splits the values into two sets, based on the return value of the function (True/False). e.g.: >>> partition(lambda x: x > 3, range(5)) [0, 1, 2, 3], [4] """ results = ([], []) for item in values: results[predicate(item)].append(item) return results if sys.version_info >= (2, 7, 2): from functools import total_ordering else: # For Python < 2.7.2. total_ordering in versions prior to 2.7.2 is buggy. # See http://bugs.python.org/issue10042 for details. For these versions use # code borrowed from Python 2.7.3. def total_ordering(cls): """Class decorator that fills in missing ordering methods""" convert = { '__lt__': [('__gt__', lambda self, other: not (self < other or self == other)), ('__le__', lambda self, other: self < other or self == other), ('__ge__', lambda self, other: not self < other)], '__le__': [('__ge__', lambda self, other: not self <= other or self == other), ('__lt__', lambda self, other: self <= other and not self == other), ('__gt__', lambda self, other: not self <= other)], '__gt__': [('__lt__', lambda self, other: not (self > other or self == other)), ('__ge__', lambda self, other: self > other or self == other), ('__le__', lambda self, other: not self > other)], '__ge__': [('__le__', lambda self, other: (not self >= other) or self == other), ('__gt__', lambda self, other: self >= other and not self == other), ('__lt__', lambda self, other: not self >= other)] } roots = set(dir(cls)) & set(convert) if not roots: raise ValueError('must define at least one ordering operation: < > <= >=') root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__ for opname, opfunc in convert[root]: if opname not in roots: opfunc.__name__ = opname opfunc.__doc__ = getattr(int, opname).__doc__ setattr(cls, opname, opfunc) return cls
Back to Top