[pypy-commit] pypy cpyext-tp_dictoffset: test, fixes for when tp_dictoffset is non-zero, when PyObject_*Attr* can run
mattip
pypy.commits at gmail.com
Mon Sep 11 11:45:29 EDT 2017
Author: Matti Picus <matti.picus at gmail.com>
Branch: cpyext-tp_dictoffset
Changeset: r92370:e2b3bea05c2b
Date: 2017-09-11 18:06 +0300
http://bitbucket.org/pypy/pypy/changeset/e2b3bea05c2b/
Log: test, fixes for when tp_dictoffset is non-zero, when PyObject_*Attr*
can run
diff --git a/pypy/module/cpyext/api.py b/pypy/module/cpyext/api.py
--- a/pypy/module/cpyext/api.py
+++ b/pypy/module/cpyext/api.py
@@ -1183,6 +1183,7 @@
def attach_recursively(space, static_pyobjs, static_objs_w, attached_objs, i):
# Start at i but make sure all the base classes are already attached
from pypy.module.cpyext.pyobject import get_typedescr, make_ref
+ from pypy.module.cpyext.typeobject import type_attach
if i in attached_objs:
return
py_obj = static_pyobjs[i]
@@ -1205,9 +1206,18 @@
typedescr = get_typedescr(w_type.layout.typedef)
py_obj.c_ob_type = rffi.cast(PyTypeObjectPtr,
make_ref(space, w_type))
- typedescr.attach(space, py_obj, w_obj)
- # prevent PyObject_SetAttr on built-ins
- rffi.cast(PyTypeObjectPtr, py_obj).c_tp_dictoffset = 0
+ if isinstance(w_obj, W_TypeObject):
+ # type and module objects need to have tp_dictoffset != 0
+ # so that, like user-defined python classes, they can have
+ # attributes set via PyObject_SetAttr()
+ set_tp_dictoffset = False
+ if w_obj is space.w_type:
+ set_tp_dictoffset = True
+ if space.is_w(w_obj, space.gettypeobject(Module.typedef)):
+ set_tp_dictoffset = True
+ type_attach(space, py_obj, w_obj, set_tp_dictoffset=set_tp_dictoffset)
+ else:
+ typedescr.attach(space, py_obj, w_obj)
attached_objs.append(i)
diff --git a/pypy/module/cpyext/object.py b/pypy/module/cpyext/object.py
--- a/pypy/module/cpyext/object.py
+++ b/pypy/module/cpyext/object.py
@@ -123,7 +123,9 @@
@cpython_api([PyObject, PyObject, PyObject], rffi.INT_real, error=-1)
def PyObject_SetAttr(space, w_obj, w_name, w_value):
pyobj = as_pyobj(space, w_obj) # assumes w_obj is kept alive for the call
- if (pyobj.c_ob_type.c_tp_dictoffset == 0):
+ if (pyobj.c_ob_type.c_tp_dictoffset == 0 and
+ not pyobj.c_ob_type.c_tp_setattro and
+ not pyobj.c_ob_type.c_tp_setattr):
raise oefmt(space.w_AttributeError,
"object has no attribute %s", space.text_w(w_name))
operation.setattr(space, w_obj, w_name, w_value)
@@ -133,7 +135,9 @@
def PyObject_SetAttrString(space, w_obj, name_ptr, w_value):
w_name = space.newtext(rffi.charp2str(name_ptr))
pyobj = as_pyobj(space, w_obj) # assumes w_obj is kept alive for the call
- if (pyobj.c_ob_type.c_tp_dictoffset == 0):
+ if (pyobj.c_ob_type.c_tp_dictoffset == 0 and
+ not pyobj.c_ob_type.c_tp_setattro and
+ not pyobj.c_ob_type.c_tp_setattr):
raise oefmt(space.w_AttributeError,
"object has no attribute %s", space.text_w(w_name))
operation.setattr(space, w_obj, w_name, w_value)
@@ -144,7 +148,9 @@
"""Delete attribute named attr_name, for object o. Returns -1 on failure.
This is the equivalent of the Python statement del o.attr_name."""
pyobj = as_pyobj(space, w_obj) # assumes w_obj is kept alive for the call
- if (pyobj.c_ob_type.c_tp_dictoffset == 0):
+ if (pyobj.c_ob_type.c_tp_dictoffset == 0 and
+ not pyobj.c_ob_type.c_tp_setattro and
+ not pyobj.c_ob_type.c_tp_setattr):
raise oefmt(space.w_AttributeError,
"object has no attribute %s", space.text_w(w_name))
space.delattr(w_obj, w_name)
@@ -156,7 +162,9 @@
This is the equivalent of the Python statement del o.attr_name."""
w_name = space.newtext(rffi.charp2str(name_ptr))
pyobj = as_pyobj(space, w_obj) # assumes w_obj is kept alive for the call
- if (pyobj.c_ob_type.c_tp_dictoffset == 0):
+ if (pyobj.c_ob_type.c_tp_dictoffset == 0 and
+ not pyobj.c_ob_type.c_tp_setattro and
+ not pyobj.c_ob_type.c_tp_setattr):
raise oefmt(space.w_AttributeError,
"object has no attribute %s", space.text_w(w_name))
space.delattr(w_obj, w_name)
diff --git a/pypy/module/cpyext/test/test_cpyext.py b/pypy/module/cpyext/test/test_cpyext.py
--- a/pypy/module/cpyext/test/test_cpyext.py
+++ b/pypy/module/cpyext/test/test_cpyext.py
@@ -584,12 +584,17 @@
Py_INCREF(t);
return t;
}
+ static PyObject * foo_get_dictoffset(PyObject *self, PyObject * args) {
+ return PyLong_FromLong(args->ob_type->tp_dictoffset);
+ }
static PyMethodDef methods[] = {
{ "test", foo_test, METH_VARARGS },
+ { "get_dictoffset", foo_get_dictoffset, METH_O },
{ NULL }
};
"""
module = self.import_module(name='foo', init=init, body=body)
+ assert module.get_dictoffset(module) > 0
assert module.test(True, True) == True
def test_exception(self):
diff --git a/pypy/module/cpyext/test/test_eval.py b/pypy/module/cpyext/test/test_eval.py
--- a/pypy/module/cpyext/test/test_eval.py
+++ b/pypy/module/cpyext/test/test_eval.py
@@ -388,6 +388,10 @@
return NULL;
}
return args->ob_type->tp_iternext(args);
+ '''),
+ ('get_dictoffset', "METH_O",
+ '''
+ return PyLong_FromLong(args->ob_type->tp_dictoffset);
'''),])
def __init__(self, N):
self.N = N
@@ -415,3 +419,4 @@
except StopIteration:
pass
assert out == [0, 1, 2, 3, 4]
+ assert module.get_dictoffset(c) > 0
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -569,18 +569,18 @@
# w_obj is an instance of w_A or one of its subclasses. So climb up the
# inheritance chain until base.c_tp_dealloc is exactly this_func, and then
# continue on up until they differ.
- #print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name)
+ # print 'subtype_dealloc, start from', rffi.charp2str(base.c_tp_name)
while base.c_tp_dealloc != this_func_ptr:
base = base.c_tp_base
assert base
- #print ' ne move to', rffi.charp2str(base.c_tp_name)
+ # print ' ne move to', rffi.charp2str(base.c_tp_name)
w_obj = from_ref(space, rffi.cast(PyObject, base))
while base.c_tp_dealloc == this_func_ptr:
base = base.c_tp_base
assert base
- #print ' eq move to', rffi.charp2str(base.c_tp_name)
+ # print ' eq move to', rffi.charp2str(base.c_tp_name)
w_obj = from_ref(space, rffi.cast(PyObject, base))
- #print ' end with', rffi.charp2str(base.c_tp_name)
+ # print ' end with', rffi.charp2str(base.c_tp_name)
dealloc = base.c_tp_dealloc
# XXX call tp_del if necessary
generic_cpy_call(space, dealloc, obj)
@@ -740,7 +740,7 @@
return rffi.cast(PyObject, heaptype)
-def type_attach(space, py_obj, w_type, w_userdata=None):
+def type_attach(space, py_obj, w_type, w_userdata=None, set_tp_dictoffset=True):
"""
Fills a newly allocated PyTypeObject from an existing type.
"""
@@ -815,7 +815,8 @@
if pto.c_tp_base != base_object_pto or flags & Py_TPFLAGS_HEAPTYPE:
pto.c_tp_new = pto.c_tp_base.c_tp_new
Py_DecRef(space, base_object_pyo)
- pto.c_tp_dictoffset = rffi.offsetof(lltype.typeOf(pto).TO, 'c_tp_dictoffset')
+ if set_tp_dictoffset:
+ pto.c_tp_dictoffset = rffi.offsetof(lltype.typeOf(pto).TO, 'c_tp_dictoffset')
pto.c_tp_flags |= Py_TPFLAGS_READY
return pto
More information about the pypy-commit
mailing list