Last active
August 29, 2015 14:05
-
-
Save gavinwahl/234e20b534745bf816a8 to your computer and use it in GitHub Desktop.
Why not look up method implementations lazily?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| diff --git a/django/utils/functional.py b/django/utils/functional.py | |
| index c512084..11a5500 100644 | |
| --- a/django/utils/functional.py | |
| +++ b/django/utils/functional.py | |
| @@ -84,13 +84,14 @@ def lazy(func, *resultclasses): | |
| called on the result of that function. The function is not evaluated | |
| until one of the methods on the result is called. | |
| """ | |
| - __dispatch = None | |
| + __prepared = False | |
| def __init__(self, args, kw): | |
| self.__args = args | |
| self.__kw = kw | |
| - if self.__dispatch is None: | |
| + if not self.__prepared: | |
| self.__prepare_class__() | |
| + self.__prepared = True | |
| def __reduce__(self): | |
| return ( | |
| @@ -100,18 +101,15 @@ def lazy(func, *resultclasses): | |
| @classmethod | |
| def __prepare_class__(cls): | |
| - cls.__dispatch = {} | |
| for resultclass in resultclasses: | |
| - cls.__dispatch[resultclass] = {} | |
| - for type_ in reversed(resultclass.mro()): | |
| - for (k, v) in type_.__dict__.items(): | |
| - # All __promise__ return the same wrapper method, but | |
| - # they also do setup, inserting the method into the | |
| - # dispatch dict. | |
| - meth = cls.__promise__(resultclass, k, v) | |
| - if hasattr(cls, k): | |
| + 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 | |
| - setattr(cls, k, meth) | |
| + meth = cls.__promise__(resultclass, 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." | |
| @@ -127,21 +125,13 @@ def lazy(func, *resultclasses): | |
| cls.__str__ = cls.__bytes_cast | |
| @classmethod | |
| - def __promise__(cls, klass, funcname, method): | |
| - # Builds a wrapper around some magic method and registers that | |
| - # magic method for the given type and method name. | |
| + def __promise__(cls, klass, 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) | |
| - for t in type(res).mro(): | |
| - if t in self.__dispatch: | |
| - return self.__dispatch[t][funcname](res, *args, **kw) | |
| - raise TypeError("Lazy object returned unexpected type.") | |
| - | |
| - if klass not in cls.__dispatch: | |
| - cls.__dispatch[klass] = {} | |
| - cls.__dispatch[klass][funcname] = method | |
| + return getattr(res, method_name)(*args, **kw) | |
| return __wrapper__ | |
| def __text_cast(self): | |
| diff --git a/tests/utils_tests/test_functional.py b/tests/utils_tests/test_functional.py | |
| index e9010b3..54754de 100644 | |
| --- a/tests/utils_tests/test_functional.py | |
| +++ b/tests/utils_tests/test_functional.py | |
| @@ -22,6 +22,20 @@ class FunctionalTestCase(unittest.TestCase): | |
| t = lazy(lambda: Klazz(), Klazz)() | |
| self.assertTrue('base_method' in dir(t)) | |
| + def test_lazy_base_class_override(self): | |
| + """Test that lazy finds the correct (overridden) method implementation""" | |
| + | |
| + class Base(object): | |
| + def method(self): | |
| + return 'Base' | |
| + | |
| + class Klazz(Base): | |
| + def method(self): | |
| + return 'Klazz' | |
| + | |
| + t = lazy(lambda: Klazz(), Base)() | |
| + self.assertEqual(t.method(), 'Klazz') | |
| + | |
| def test_lazy_property(self): | |
| class A(object): |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment