Skip to content

Commit 1ac1047

Browse files
committed
Add test for native object dicts
1 parent 51e9aa1 commit 1ac1047

File tree

2 files changed

+78
-1
lines changed

2 files changed

+78
-1
lines changed

graalpython/com.oracle.graal.python.test/src/tests/cpyext/__init__.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -754,13 +754,18 @@ def CPyExtHeapType(name, bases=(object), code='', slots=None, **kwargs):
754754
755755
{includes}
756756
757+
typedef struct {{
758+
{struct_base}
759+
{cmembers}
760+
}} {name}Object;
761+
757762
{code}
758763
759764
PyType_Slot slots[] = {{
760765
{slots}
761766
}};
762767
763-
PyType_Spec spec = {{ "{name}Type", sizeof(PyHeapTypeObject), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots }};
768+
PyType_Spec spec = {{ "{name}Type", sizeof({name}Object), 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots }};
764769
765770
static PyObject* create(PyObject* unused, PyObject* bases) {{
766771
{ready_code}
@@ -798,6 +803,8 @@ def CPyExtHeapType(name, bases=(object), code='', slots=None, **kwargs):
798803
kwargs["code"] = code
799804
kwargs["slots"] = '{0}' if slots is None else ',\n'.join(slots + ['{0}'])
800805
kwargs.setdefault("includes", "")
806+
kwargs.setdefault("struct_base", "PyObject_HEAD")
807+
kwargs.setdefault("cmembers", "")
801808
kwargs.setdefault("ready_code", "")
802809
kwargs.setdefault("post_ready_code", "")
803810
code = UnseenFormatter().format(template, **kwargs)
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
from tests.cpyext import CPyExtType, CPyExtHeapType, assert_raises
2+
3+
NativeTypeWithoutDict = CPyExtType(
4+
name='NativeTypeWithoutDict',
5+
)
6+
7+
NativeTypeWithDict = CPyExtType(
8+
name='NativeTypeWithDict',
9+
cmembers='PyObject* dict;',
10+
tp_dictoffset='offsetof(NativeTypeWithDictObject, dict)',
11+
tp_getset='{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict}',
12+
)
13+
14+
NativeHeapTypeWithoutDict = CPyExtHeapType(
15+
name='NativeTypeWithManagedDict',
16+
)
17+
18+
NativeHeapTypeWithDict = CPyExtHeapType(
19+
name='NativeHeapTypeWithManagedDict',
20+
cmembers='PyObject* dict;',
21+
code='''
22+
static PyMemberDef memberlist[] = {
23+
{"__dictoffset__", T_PYSSIZET, offsetof(NativeHeapTypeWithManagedDictObject, dict), READONLY},
24+
{NULL}
25+
};
26+
static PyGetSetDef getsetlist[] = {
27+
{"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
28+
{NULL}
29+
};
30+
''',
31+
slots=[
32+
'{Py_tp_members, memberlist}',
33+
'{Py_tp_getset, getsetlist}',
34+
],
35+
)
36+
37+
38+
# TODO it would be great if we could test creating heap types with Py_TPFLAGS_MANAGED_DICT, because pybind11 does that,
39+
# but there's no way to do that without abusing abusing implementations details
40+
41+
42+
class NativeSubtypeWithDict(NativeTypeWithDict):
43+
pass
44+
45+
46+
class NativeSubtypeWithAddedDict(NativeTypeWithoutDict):
47+
pass
48+
49+
50+
class TestObjectDict:
51+
@staticmethod
52+
def assert_has_no_dict(obj):
53+
assert_raises(AttributeError, setattr, obj, 'foo', 1)
54+
assert_raises(AttributeError, getattr, obj, '__dict__')
55+
56+
@staticmethod
57+
def assert_has_working_dict(obj):
58+
obj.foo = 1
59+
assert obj.foo == 1
60+
assert obj.__dict__ == {'foo': 1}
61+
62+
def test_no_dict(self):
63+
self.assert_has_no_dict(NativeTypeWithoutDict())
64+
self.assert_has_no_dict(NativeHeapTypeWithoutDict())
65+
66+
def test_dict(self):
67+
self.assert_has_working_dict(NativeTypeWithDict())
68+
self.assert_has_working_dict(NativeHeapTypeWithDict())
69+
self.assert_has_working_dict(NativeSubtypeWithDict())
70+
self.assert_has_working_dict(NativeSubtypeWithAddedDict())

0 commit comments

Comments
 (0)