Fixed #28358 -- Prevented LazyObject from mimicking nonexistent attributes.
Thanks Sergey Fedoseev for the initial patch.
This commit is contained in:
parent
1d071ec1aa
commit
97d7990abd
|
@ -157,8 +157,9 @@ class LazySettings(LazyObject):
|
||||||
def USE_L10N(self):
|
def USE_L10N(self):
|
||||||
stack = traceback.extract_stack()
|
stack = traceback.extract_stack()
|
||||||
# Show a warning if the setting is used outside of Django.
|
# Show a warning if the setting is used outside of Django.
|
||||||
# Stack index: -1 this line, -2 the caller.
|
# Stack index: -1 this line, -2 the LazyObject __getattribute__(),
|
||||||
filename, _, _, _ = stack[-2]
|
# -3 the caller.
|
||||||
|
filename, _, _, _ = stack[-3]
|
||||||
if not filename.startswith(os.path.dirname(django.__file__)):
|
if not filename.startswith(os.path.dirname(django.__file__)):
|
||||||
warnings.warn(
|
warnings.warn(
|
||||||
USE_L10N_DEPRECATED_MSG,
|
USE_L10N_DEPRECATED_MSG,
|
||||||
|
|
|
@ -266,6 +266,7 @@ def new_method_proxy(func):
|
||||||
self._setup()
|
self._setup()
|
||||||
return func(self._wrapped, *args)
|
return func(self._wrapped, *args)
|
||||||
|
|
||||||
|
inner._mask_wrapped = False
|
||||||
return inner
|
return inner
|
||||||
|
|
||||||
|
|
||||||
|
@ -286,6 +287,14 @@ class LazyObject:
|
||||||
# override __copy__() and __deepcopy__() as well.
|
# override __copy__() and __deepcopy__() as well.
|
||||||
self._wrapped = empty
|
self._wrapped = empty
|
||||||
|
|
||||||
|
def __getattribute__(self, name):
|
||||||
|
value = super().__getattribute__(name)
|
||||||
|
# If attribute is a proxy method, raise an AttributeError to call
|
||||||
|
# __getattr__() and use the wrapped object method.
|
||||||
|
if not getattr(value, "_mask_wrapped", True):
|
||||||
|
raise AttributeError
|
||||||
|
return value
|
||||||
|
|
||||||
__getattr__ = new_method_proxy(getattr)
|
__getattr__ = new_method_proxy(getattr)
|
||||||
|
|
||||||
def __setattr__(self, name, value):
|
def __setattr__(self, name, value):
|
||||||
|
|
|
@ -32,6 +32,28 @@ class LazyObjectTestCase(TestCase):
|
||||||
|
|
||||||
return AdHocLazyObject()
|
return AdHocLazyObject()
|
||||||
|
|
||||||
|
def test_getattribute(self):
|
||||||
|
"""
|
||||||
|
Proxy methods don't exist on wrapped objects unless they're set.
|
||||||
|
"""
|
||||||
|
attrs = [
|
||||||
|
"__getitem__",
|
||||||
|
"__setitem__",
|
||||||
|
"__delitem__",
|
||||||
|
"__iter__",
|
||||||
|
"__len__",
|
||||||
|
"__contains__",
|
||||||
|
]
|
||||||
|
foo = Foo()
|
||||||
|
obj = self.lazy_wrap(foo)
|
||||||
|
for attr in attrs:
|
||||||
|
with self.subTest(attr):
|
||||||
|
self.assertFalse(hasattr(obj, attr))
|
||||||
|
setattr(foo, attr, attr)
|
||||||
|
obj_with_attr = self.lazy_wrap(foo)
|
||||||
|
self.assertTrue(hasattr(obj_with_attr, attr))
|
||||||
|
self.assertEqual(getattr(obj_with_attr, attr), attr)
|
||||||
|
|
||||||
def test_getattr(self):
|
def test_getattr(self):
|
||||||
obj = self.lazy_wrap(Foo())
|
obj = self.lazy_wrap(Foo())
|
||||||
self.assertEqual(obj.foo, "bar")
|
self.assertEqual(obj.foo, "bar")
|
||||||
|
|
Loading…
Reference in New Issue