Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 224205f

Browse files
committed
Merged revisions 72461 via svnmerge from
svn+ssh://[email protected]/python/trunk ........ r72461 | benjamin.peterson | 2009-05-07 22:06:00 -0500 (Thu, 07 May 2009) | 1 line add _PyObject_LookupSpecial to handle fetching special method lookup ........
1 parent c04dad7 commit 224205f

4 files changed

Lines changed: 63 additions & 9 deletions

File tree

Include/object.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ PyAPI_FUNC(PyObject *) PyType_GenericAlloc(PyTypeObject *, Py_ssize_t);
414414
PyAPI_FUNC(PyObject *) PyType_GenericNew(PyTypeObject *,
415415
PyObject *, PyObject *);
416416
PyAPI_FUNC(PyObject *) _PyType_Lookup(PyTypeObject *, PyObject *);
417+
PyAPI_FUNC(PyObject *) _PyObject_LookupSpecial(PyObject *, char *, PyObject **);
417418
PyAPI_FUNC(unsigned int) PyType_ClearCache(void);
418419
PyAPI_FUNC(void) PyType_Modified(PyTypeObject *);
419420

Lib/test/test_descr.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1538,6 +1538,58 @@ class E(object):
15381538
self.assertEqual(E().foo.__func__, C.foo) # i.e., unbound
15391539
self.assert_(repr(C.foo.__get__(C(1))).startswith("<bound method "))
15401540

1541+
def test_special_method_lookup(self):
1542+
# The lookup of special methods bypasses __getattr__ and
1543+
# __getattribute__, but they still can be descriptors.
1544+
1545+
def run_context(manager):
1546+
with manager:
1547+
pass
1548+
def iden(self):
1549+
return self
1550+
def hello(self):
1551+
return b"hello"
1552+
1553+
# It would be nice to have every special method tested here, but I'm
1554+
# only listing the ones I can remember outside of typeobject.c, since it
1555+
# does it right.
1556+
specials = [
1557+
("__bytes__", bytes, hello),
1558+
# These two fail because the compiler generates LOAD_ATTR to look
1559+
# them up. We'd have to add a new opcode to fix this, and it's
1560+
# probably not worth it.
1561+
# ("__enter__", run_context, iden),
1562+
# ("__exit__", run_context, iden),
1563+
]
1564+
1565+
class Checker(object):
1566+
def __getattr__(self, attr, test=self):
1567+
test.fail("__getattr__ called with {0}".format(attr))
1568+
def __getattribute__(self, attr, test=self):
1569+
test.fail("__getattribute__ called with {0}".format(attr))
1570+
class SpecialDescr(object):
1571+
def __init__(self, impl):
1572+
self.impl = impl
1573+
def __get__(self, obj, owner):
1574+
record.append(1)
1575+
return self
1576+
def __call__(self, *args):
1577+
return self.impl(*args)
1578+
1579+
1580+
for name, runner, meth_impl in specials:
1581+
class X(Checker):
1582+
pass
1583+
setattr(X, name, staticmethod(meth_impl))
1584+
runner(X())
1585+
1586+
record = []
1587+
class X(Checker):
1588+
pass
1589+
setattr(X, name, SpecialDescr(meth_impl))
1590+
runner(X())
1591+
self.assertEqual(record, [1], name)
1592+
15411593
def test_specials(self):
15421594
# Testing special operators...
15431595
# Test operators like __hash__ for which a built-in default exists

Objects/object.c

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -474,12 +474,6 @@ PyObject_Bytes(PyObject *v)
474474
PyObject *result, *func;
475475
static PyObject *bytesstring = NULL;
476476

477-
if (bytesstring == NULL) {
478-
bytesstring = PyUnicode_InternFromString("__bytes__");
479-
if (bytesstring == NULL)
480-
return NULL;
481-
}
482-
483477
if (v == NULL)
484478
return PyBytes_FromString("<NULL>");
485479

@@ -488,10 +482,10 @@ PyObject_Bytes(PyObject *v)
488482
return v;
489483
}
490484

491-
/* Doesn't create a reference */
492-
func = _PyType_Lookup(Py_TYPE(v), bytesstring);
485+
func = _PyObject_LookupSpecial(v, "__bytes__", &bytesstring);
493486
if (func != NULL) {
494487
result = PyObject_CallFunctionObjArgs(func, v, NULL);
488+
Py_DECREF(func);
495489
if (result == NULL)
496490
return NULL;
497491
if (!PyBytes_Check(result)) {
@@ -503,7 +497,6 @@ PyObject_Bytes(PyObject *v)
503497
}
504498
return result;
505499
}
506-
PyErr_Clear();
507500
return PyBytes_FromObject(v);
508501
}
509502

Objects/typeobject.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1125,6 +1125,8 @@ PyType_IsSubtype(PyTypeObject *a, PyTypeObject *b)
11251125
when the _PyType_Lookup() call fails;
11261126
11271127
- lookup_method() always raises an exception upon errors.
1128+
1129+
- _PyObject_LookupSpecial() exported for the benefit of other places.
11281130
*/
11291131

11301132
static PyObject *
@@ -1157,6 +1159,12 @@ lookup_method(PyObject *self, char *attrstr, PyObject **attrobj)
11571159
return res;
11581160
}
11591161

1162+
PyObject *
1163+
_PyObject_LookupSpecial(PyObject *self, char *attrstr, PyObject **attrobj)
1164+
{
1165+
return lookup_maybe(self, attrstr, attrobj);
1166+
}
1167+
11601168
/* A variation of PyObject_CallMethod that uses lookup_method()
11611169
instead of PyObject_GetAttrString(). This uses the same convention
11621170
as lookup_method to cache the interned name string object. */

0 commit comments

Comments
 (0)