Commit 8f641227 authored by Stefan Behnel's avatar Stefan Behnel

use more CPython-like (and potentially faster) _PyType_Lookup() for internal...

use more CPython-like (and potentially faster) _PyType_Lookup() for internal special method lookups instead of generic attribute lookup
parent 5257cc32
......@@ -4991,6 +4991,7 @@ class AttributeNode(ExprNode):
is_called = 0
needs_none_check = True
is_memslice_transpose = False
is_special_lookup = False
def as_cython_attribute(self):
if (isinstance(self.obj, NameNode) and
......@@ -5359,11 +5360,18 @@ class AttributeNode(ExprNode):
def generate_result_code(self, code):
if self.is_py_attr:
code.globalstate.use_utility_code(
UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c"))
if self.is_special_lookup:
code.globalstate.use_utility_code(
UtilityCode.load_cached("PyObjectLookupSpecial", "ObjectHandling.c"))
lookup_func_name = '__Pyx_PyObject_LookupSpecial'
else:
code.globalstate.use_utility_code(
UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c"))
lookup_func_name = '__Pyx_PyObject_GetAttrStr'
code.putln(
'%s = __Pyx_PyObject_GetAttrStr(%s, %s); %s' % (
'%s = %s(%s, %s); %s' % (
self.result(),
lookup_func_name,
self.obj.py_result(),
code.intern_identifier(self.attribute),
code.error_goto_if_null(self.result(), self.pos)))
......
......@@ -5919,8 +5919,8 @@ class WithStatNode(StatNode):
self.manager.generate_evaluation_code(code)
self.exit_var = code.funcstate.allocate_temp(py_object_type, manage_ref=False)
code.globalstate.use_utility_code(
UtilityCode.load_cached("PyObjectGetAttrStr", "ObjectHandling.c"))
code.putln("%s = __Pyx_PyObject_GetAttrStr(%s, %s); %s" % (
UtilityCode.load_cached("PyObjectLookupSpecial", "ObjectHandling.c"))
code.putln("%s = __Pyx_PyObject_LookupSpecial(%s, %s); %s" % (
self.exit_var,
self.manager.py_result(),
code.intern_identifier(EncodedString('__exit__')),
......
......@@ -1212,11 +1212,12 @@ class WithTransform(CythonTransform, SkipDeclarations):
pos = node.pos
body, target, manager = node.body, node.target, node.manager
node.enter_call = ExprNodes.SimpleCallNode(
pos, function = ExprNodes.AttributeNode(
pos, obj = ExprNodes.CloneNode(manager),
attribute = EncodedString('__enter__')),
args = [],
is_temp = True)
pos, function=ExprNodes.AttributeNode(
pos, obj=ExprNodes.CloneNode(manager),
attribute=EncodedString('__enter__'),
is_special_lookup=True),
args=[],
is_temp=True)
if target is not None:
body = Nodes.StatListNode(
pos, stats = [
......
......@@ -1038,6 +1038,35 @@ static CYTHON_INLINE PyObject *__Pyx_GetAttr(PyObject *o, PyObject *n) {
return PyObject_GetAttr(o, n);
}
/////////////// PyObjectLookupSpecial.proto ///////////////
//@requires: PyObjectGetAttrStr
#if CYTHON_COMPILING_IN_CPYTHON
static CYTHON_INLINE PyObject* __Pyx_PyObject_LookupSpecial(PyObject* obj, PyObject* attr_name) {
PyObject *res;
PyTypeObject *tp = Py_TYPE(obj);
#if PY_MAJOR_VERSION < 3
if (unlikely(PyInstance_Check(obj)))
return __Pyx_PyObject_GetAttrStr(obj, attr_name);
#endif
// adapted from CPython's special_lookup() in ceval.c
res = _PyType_Lookup(tp, attr_name);
if (likely(res)) {
descrgetfunc f = Py_TYPE(res)->tp_descr_get;
if (!f) {
Py_INCREF(res);
} else {
res = f(res, obj, (PyObject *)tp);
}
} else {
PyErr_SetObject(PyExc_AttributeError, attr_name);
}
return res;
}
#else
#define __Pyx_PyObject_LookupSpecial(o,n) PyObject_GetAttr(o,n)
#endif
/////////////// PyObjectGetAttrStr.proto ///////////////
#if CYTHON_COMPILING_IN_CPYTHON
......
# mode: run
# tag: pyclass, getattr
"""
Python bypasses __getattribute__ overrides for some special method lookups.
"""
lookups = []
class PyClass(object):
"""
>>> del lookups[:]
>>> obj = PyClass()
>>> obj.test
'getattribute(test)'
>>> lookups
['getattribute(test)']
"""
def __getattribute__(self, name):
lookup = 'getattribute(%s)' % name
lookups.append(lookup)
return lookup
def __getattr__(self, name):
lookup = 'getattr(%s)' % name
lookups.append(lookup)
return lookup
def use_as_context_manager(obj):
"""
>>> del lookups[:]
>>> class PyCM(PyClass):
... def __enter__(self): return '__enter__(%s)' % (self is obj or self)
... def __exit__(self, *args): pass
>>> obj = PyCM()
>>> use_as_context_manager(obj)
'__enter__(True)'
>>> lookups
[]
"""
with obj as x:
pass
return x
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment