backport r67246 from the trunk
This commit is contained in:
parent
a8629ce4b8
commit
42fe1a2ff8
|
@ -1073,20 +1073,21 @@ def consistency_with_epg():
|
||||||
(EditableScrollablePane, ScrollablePane, EditablePane,
|
(EditableScrollablePane, ScrollablePane, EditablePane,
|
||||||
Pane, ScrollingMixin, EditingMixin, object))
|
Pane, ScrollingMixin, EditingMixin, object))
|
||||||
|
|
||||||
|
def raises(exc, expected, callable, *args):
|
||||||
|
try:
|
||||||
|
callable(*args)
|
||||||
|
except exc, msg:
|
||||||
|
if not str(msg).startswith(expected):
|
||||||
|
raise TestFailed, "Message %r, expected %r" % (str(msg),
|
||||||
|
expected)
|
||||||
|
else:
|
||||||
|
raise TestFailed, "Expected %s" % exc
|
||||||
|
|
||||||
mro_err_msg = """Cannot create a consistent method resolution
|
mro_err_msg = """Cannot create a consistent method resolution
|
||||||
order (MRO) for bases """
|
order (MRO) for bases """
|
||||||
|
|
||||||
def mro_disagreement():
|
def mro_disagreement():
|
||||||
if verbose: print "Testing error messages for MRO disagreement..."
|
if verbose: print "Testing error messages for MRO disagreement..."
|
||||||
def raises(exc, expected, callable, *args):
|
|
||||||
try:
|
|
||||||
callable(*args)
|
|
||||||
except exc, msg:
|
|
||||||
if not str(msg).startswith(expected):
|
|
||||||
raise TestFailed, "Message %r, expected %r" % (str(msg),
|
|
||||||
expected)
|
|
||||||
else:
|
|
||||||
raise TestFailed, "Expected %s" % exc
|
|
||||||
class A(object): pass
|
class A(object): pass
|
||||||
class B(A): pass
|
class B(A): pass
|
||||||
class C(object): pass
|
class C(object): pass
|
||||||
|
@ -4171,6 +4172,45 @@ def methodwrapper():
|
||||||
vereq(t.__add__, (7,).__add__)
|
vereq(t.__add__, (7,).__add__)
|
||||||
vereq(hash(t.__add__), hash((7,).__add__))
|
vereq(hash(t.__add__), hash((7,).__add__))
|
||||||
|
|
||||||
|
def test_getattr_hooks():
|
||||||
|
# issue 4230
|
||||||
|
class Descriptor(object):
|
||||||
|
counter = 0
|
||||||
|
def __get__(self, obj, objtype=None):
|
||||||
|
def getter(name):
|
||||||
|
self.counter += 1
|
||||||
|
raise AttributeError(name)
|
||||||
|
return getter
|
||||||
|
|
||||||
|
descr = Descriptor()
|
||||||
|
class A(object):
|
||||||
|
__getattribute__ = descr
|
||||||
|
class B(object):
|
||||||
|
__getattr__ = descr
|
||||||
|
class C(object):
|
||||||
|
__getattribute__ = descr
|
||||||
|
__getattr__ = descr
|
||||||
|
|
||||||
|
raises(AttributeError, "attr", getattr, A(), "attr")
|
||||||
|
vereq(descr.counter, 1)
|
||||||
|
raises(AttributeError, "attr", getattr, B(), "attr")
|
||||||
|
vereq(descr.counter, 2)
|
||||||
|
raises(AttributeError, "attr", getattr, C(), "attr")
|
||||||
|
vereq(descr.counter, 4)
|
||||||
|
|
||||||
|
import gc
|
||||||
|
class EvilGetattribute(object):
|
||||||
|
# This used to segfault
|
||||||
|
def __getattr__(self, name):
|
||||||
|
raise AttributeError(name)
|
||||||
|
def __getattribute__(self, name):
|
||||||
|
del EvilGetattribute.__getattr__
|
||||||
|
for i in range(5):
|
||||||
|
gc.collect()
|
||||||
|
raise AttributeError(name)
|
||||||
|
|
||||||
|
raises(AttributeError, "attr", getattr, EvilGetattribute(), "attr")
|
||||||
|
|
||||||
def notimplemented():
|
def notimplemented():
|
||||||
# all binary methods should be able to return a NotImplemented
|
# all binary methods should be able to return a NotImplemented
|
||||||
if verbose:
|
if verbose:
|
||||||
|
@ -4352,6 +4392,7 @@ def test_main():
|
||||||
methodwrapper()
|
methodwrapper()
|
||||||
notimplemented()
|
notimplemented()
|
||||||
test_assign_slice()
|
test_assign_slice()
|
||||||
|
test_getattr_hooks()
|
||||||
|
|
||||||
if verbose: print "All OK"
|
if verbose: print "All OK"
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ What's New in Python 2.5.3?
|
||||||
Core and builtins
|
Core and builtins
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
|
- Issue #4230: If ``__getattr__`` is a descriptor, it now functions correctly.
|
||||||
|
|
||||||
- Issue #4048: The parser module now correctly validates relative imports.
|
- Issue #4048: The parser module now correctly validates relative imports.
|
||||||
|
|
||||||
- Issue #4176: Fixed a crash when pickling an object which ``__reduce__``
|
- Issue #4176: Fixed a crash when pickling an object which ``__reduce__``
|
||||||
|
|
|
@ -4737,6 +4737,24 @@ slot_tp_getattro(PyObject *self, PyObject *name)
|
||||||
"(O)", name);
|
"(O)", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
call_attribute(PyObject *self, PyObject *attr, PyObject *name)
|
||||||
|
{
|
||||||
|
PyObject *res, *descr = NULL;
|
||||||
|
descrgetfunc f = attr->ob_type->tp_descr_get;
|
||||||
|
|
||||||
|
if (f != NULL) {
|
||||||
|
descr = f(attr, self, (PyObject *)(self->ob_type));
|
||||||
|
if (descr == NULL)
|
||||||
|
return NULL;
|
||||||
|
else
|
||||||
|
attr = descr;
|
||||||
|
}
|
||||||
|
res = PyObject_CallFunctionObjArgs(attr, name, NULL);
|
||||||
|
Py_XDECREF(descr);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
static PyObject *
|
static PyObject *
|
||||||
slot_tp_getattr_hook(PyObject *self, PyObject *name)
|
slot_tp_getattr_hook(PyObject *self, PyObject *name)
|
||||||
{
|
{
|
||||||
|
@ -4756,24 +4774,39 @@ slot_tp_getattr_hook(PyObject *self, PyObject *name)
|
||||||
if (getattribute_str == NULL)
|
if (getattribute_str == NULL)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* speed hack: we could use lookup_maybe, but that would resolve the
|
||||||
|
method fully for each attribute lookup for classes with
|
||||||
|
__getattr__, even when the attribute is present. So we use
|
||||||
|
_PyType_Lookup and create the method only when needed, with
|
||||||
|
call_attribute. */
|
||||||
getattr = _PyType_Lookup(tp, getattr_str);
|
getattr = _PyType_Lookup(tp, getattr_str);
|
||||||
if (getattr == NULL) {
|
if (getattr == NULL) {
|
||||||
/* No __getattr__ hook: use a simpler dispatcher */
|
/* No __getattr__ hook: use a simpler dispatcher */
|
||||||
tp->tp_getattro = slot_tp_getattro;
|
tp->tp_getattro = slot_tp_getattro;
|
||||||
return slot_tp_getattro(self, name);
|
return slot_tp_getattro(self, name);
|
||||||
}
|
}
|
||||||
|
Py_INCREF(getattr);
|
||||||
|
/* speed hack: we could use lookup_maybe, but that would resolve the
|
||||||
|
method fully for each attribute lookup for classes with
|
||||||
|
__getattr__, even when self has the default __getattribute__
|
||||||
|
method. So we use _PyType_Lookup and create the method only when
|
||||||
|
needed, with call_attribute. */
|
||||||
getattribute = _PyType_Lookup(tp, getattribute_str);
|
getattribute = _PyType_Lookup(tp, getattribute_str);
|
||||||
if (getattribute == NULL ||
|
if (getattribute == NULL ||
|
||||||
(getattribute->ob_type == &PyWrapperDescr_Type &&
|
(getattribute->ob_type == &PyWrapperDescr_Type &&
|
||||||
((PyWrapperDescrObject *)getattribute)->d_wrapped ==
|
((PyWrapperDescrObject *)getattribute)->d_wrapped ==
|
||||||
(void *)PyObject_GenericGetAttr))
|
(void *)PyObject_GenericGetAttr))
|
||||||
res = PyObject_GenericGetAttr(self, name);
|
res = PyObject_GenericGetAttr(self, name);
|
||||||
else
|
else {
|
||||||
res = PyObject_CallFunctionObjArgs(getattribute, self, name, NULL);
|
Py_INCREF(getattribute);
|
||||||
|
res = call_attribute(self, getattribute, name);
|
||||||
|
Py_DECREF(getattribute);
|
||||||
|
}
|
||||||
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
if (res == NULL && PyErr_ExceptionMatches(PyExc_AttributeError)) {
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
res = PyObject_CallFunctionObjArgs(getattr, self, name, NULL);
|
res = call_attribute(self, getattr, name);
|
||||||
}
|
}
|
||||||
|
Py_DECREF(getattr);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue