From b214845f0f8d973e527e41730ca5770fad51ec5d Mon Sep 17 00:00:00 2001 From: Ran Benita Date: Fri, 3 May 2019 13:24:43 +0300 Subject: [PATCH] Improved order of methods in proxy class in lazy(). This order reads more naturally and puts methods into three groups: 1. The methods required to support the implementation of __proxy__, e.g. __deepcopy__ doesn't come from `object` and __reduce__ is overridden to support behavior required explicitly for pickling of lazy objects. 2. Methods that are specifically overridden from `object` which we don't want to inherit from the provided resultclasses. These will be skipped later when we add methods from the resultclasses. 3. Additional methods - that is _add__, __radd__, and __mod__ - don't come from `object`, but typically from `str` and `int` which are the most common use cases. Co-authored-by: Nick Pope --- django/utils/functional.py | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/django/utils/functional.py b/django/utils/functional.py index 6ee630c021..e3be672282 100644 --- a/django/utils/functional.py +++ b/django/utils/functional.py @@ -104,8 +104,12 @@ def lazy(func, *resultclasses): (func, self.__args, self.__kw) + resultclasses, ) - def __repr__(self): - return repr(self.__cast()) + 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 @classmethod def __prepare_class__(cls): @@ -133,6 +137,12 @@ def lazy(func, *resultclasses): def __cast(self): return func(*self.__args, **self.__kw) + # Explicitly wrap methods which are defined on object and hence would + # not have been overloaded by the loop over resultclasses below. + + def __repr__(self): + return repr(self.__cast()) + def __str__(self): # object defines __str__(), so __prepare_class__() won't overload # a __str__() method from the proxied class. @@ -171,8 +181,8 @@ def lazy(func, *resultclasses): def __hash__(self): return hash(self.__cast()) - def __mod__(self, rhs): - return self.__cast() % rhs + # Explicitly wrap methods which are required for certain operations on + # int/str objects to function correctly. def __add__(self, other): return self.__cast() + other @@ -180,12 +190,8 @@ def lazy(func, *resultclasses): def __radd__(self, other): return other + self.__cast() - 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 + def __mod__(self, other): + return self.__cast() % other @wraps(func) def __wrapper__(*args, **kw):