• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #include "parts.h"
2 #include <stddef.h>               // offsetof()
3 
4 
5 static struct PyModuleDef *_testcapimodule = NULL;  // set at initialization
6 
7 /* Tests for heap types (PyType_From*) */
8 
pytype_fromspec_meta(PyObject * self,PyObject * meta)9 static PyObject *pytype_fromspec_meta(PyObject* self, PyObject *meta)
10 {
11     if (!PyType_Check(meta)) {
12         PyErr_SetString(
13             PyExc_TypeError,
14             "pytype_fromspec_meta: must be invoked with a type argument!");
15         return NULL;
16     }
17 
18     PyType_Slot HeapCTypeViaMetaclass_slots[] = {
19         {0},
20     };
21 
22     PyType_Spec HeapCTypeViaMetaclass_spec = {
23         "_testcapi.HeapCTypeViaMetaclass",
24         sizeof(PyObject),
25         0,
26         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
27         HeapCTypeViaMetaclass_slots
28     };
29 
30     return PyType_FromMetaclass(
31         (PyTypeObject *) meta, NULL, &HeapCTypeViaMetaclass_spec, NULL);
32 }
33 
34 
35 static PyType_Slot empty_type_slots[] = {
36     {0, 0},
37 };
38 
39 static PyType_Spec MinimalMetaclass_spec = {
40     .name = "_testcapi.MinimalMetaclass",
41     .basicsize = sizeof(PyHeapTypeObject),
42     .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
43     .slots = empty_type_slots,
44 };
45 
46 static PyType_Spec MinimalType_spec = {
47     .name = "_testcapi.MinimalSpecType",
48     .basicsize = 0,  // Updated later
49     .flags = Py_TPFLAGS_DEFAULT,
50     .slots = empty_type_slots,
51 };
52 
53 
54 static PyObject *
test_from_spec_metatype_inheritance(PyObject * self,PyObject * Py_UNUSED (ignored))55 test_from_spec_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(ignored))
56 {
57     PyObject *metaclass = NULL;
58     PyObject *class = NULL;
59     PyObject *new = NULL;
60     PyObject *subclasses = NULL;
61     PyObject *result = NULL;
62     int r;
63 
64     metaclass = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
65     if (metaclass == NULL) {
66         goto finally;
67     }
68     class = PyObject_CallFunction(metaclass, "s(){}", "TestClass");
69     if (class == NULL) {
70         goto finally;
71     }
72 
73     MinimalType_spec.basicsize = (int)(((PyTypeObject*)class)->tp_basicsize);
74     new = PyType_FromSpecWithBases(&MinimalType_spec, class);
75     if (new == NULL) {
76         goto finally;
77     }
78     if (Py_TYPE(new) != (PyTypeObject*)metaclass) {
79         PyErr_SetString(PyExc_AssertionError,
80                 "Metaclass not set properly!");
81         goto finally;
82     }
83 
84     /* Assert that __subclasses__ is updated */
85     subclasses = PyObject_CallMethod(class, "__subclasses__", "");
86     if (!subclasses) {
87         goto finally;
88     }
89     r = PySequence_Contains(subclasses, new);
90     if (r < 0) {
91         goto finally;
92     }
93     if (r == 0) {
94         PyErr_SetString(PyExc_AssertionError,
95                 "subclasses not set properly!");
96         goto finally;
97     }
98 
99     result = Py_NewRef(Py_None);
100 
101 finally:
102     Py_XDECREF(metaclass);
103     Py_XDECREF(class);
104     Py_XDECREF(new);
105     Py_XDECREF(subclasses);
106     return result;
107 }
108 
109 
110 static PyObject *
test_from_spec_invalid_metatype_inheritance(PyObject * self,PyObject * Py_UNUSED (ignored))111 test_from_spec_invalid_metatype_inheritance(PyObject *self, PyObject *Py_UNUSED(ignored))
112 {
113     PyObject *metaclass_a = NULL;
114     PyObject *metaclass_b = NULL;
115     PyObject *class_a = NULL;
116     PyObject *class_b = NULL;
117     PyObject *bases = NULL;
118     PyObject *new = NULL;
119     PyObject *meta_error_string = NULL;
120     PyObject *exc = NULL;
121     PyObject *result = NULL;
122     PyObject *message = NULL;
123     PyObject *args = NULL;
124 
125     metaclass_a = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
126     if (metaclass_a == NULL) {
127         goto finally;
128     }
129     metaclass_b = PyType_FromSpecWithBases(&MinimalMetaclass_spec, (PyObject*)&PyType_Type);
130     if (metaclass_b == NULL) {
131         goto finally;
132     }
133     class_a = PyObject_CallFunction(metaclass_a, "s(){}", "TestClassA");
134     if (class_a == NULL) {
135         goto finally;
136     }
137 
138     class_b = PyObject_CallFunction(metaclass_b, "s(){}", "TestClassB");
139     if (class_b == NULL) {
140         goto finally;
141     }
142 
143     bases = PyTuple_Pack(2, class_a, class_b);
144     if (bases == NULL) {
145         goto finally;
146     }
147 
148     /*
149      * The following should raise a TypeError due to a MetaClass conflict.
150      */
151     new = PyType_FromSpecWithBases(&MinimalType_spec, bases);
152     if (new != NULL) {
153         PyErr_SetString(PyExc_AssertionError,
154                 "MetaType conflict not recognized by PyType_FromSpecWithBases");
155             goto finally;
156     }
157 
158     // Assert that the correct exception was raised
159     if (PyErr_ExceptionMatches(PyExc_TypeError)) {
160         exc = PyErr_GetRaisedException();
161         args = PyException_GetArgs(exc);
162         if (!PyTuple_Check(args) || PyTuple_Size(args) != 1) {
163             PyErr_SetString(PyExc_AssertionError,
164                     "TypeError args are not a one-tuple");
165             goto finally;
166         }
167         message = Py_NewRef(PyTuple_GET_ITEM(args, 0));
168         meta_error_string = PyUnicode_FromString("metaclass conflict:");
169         if (meta_error_string == NULL) {
170             goto finally;
171         }
172         int res = PyUnicode_Contains(message, meta_error_string);
173         if (res < 0) {
174             goto finally;
175         }
176         if (res == 0) {
177             PyErr_SetString(PyExc_AssertionError,
178                     "TypeError did not include expected message.");
179             goto finally;
180         }
181         result = Py_NewRef(Py_None);
182     }
183 finally:
184     Py_XDECREF(metaclass_a);
185     Py_XDECREF(metaclass_b);
186     Py_XDECREF(bases);
187     Py_XDECREF(new);
188     Py_XDECREF(meta_error_string);
189     Py_XDECREF(exc);
190     Py_XDECREF(message);
191     Py_XDECREF(class_a);
192     Py_XDECREF(class_b);
193     Py_XDECREF(args);
194     return result;
195 }
196 
197 
198 static PyObject *
simple_str(PyObject * self)199 simple_str(PyObject *self) {
200     return PyUnicode_FromString("<test>");
201 }
202 
203 
204 static PyObject *
test_type_from_ephemeral_spec(PyObject * self,PyObject * Py_UNUSED (ignored))205 test_type_from_ephemeral_spec(PyObject *self, PyObject *Py_UNUSED(ignored))
206 {
207     // Test that a heap type can be created from a spec that's later deleted
208     // (along with all its contents).
209     // All necessary data must be copied and held by the class
210     PyType_Spec *spec = NULL;
211     char *name = NULL;
212     char *doc = NULL;
213     PyType_Slot *slots = NULL;
214     PyObject *class = NULL;
215     PyObject *instance = NULL;
216     PyObject *obj = NULL;
217     PyObject *result = NULL;
218 
219     /* create a spec (and all its contents) on the heap */
220 
221     const char NAME[] = "testcapi._Test";
222     const char DOC[] = "a test class";
223 
224     spec = PyMem_New(PyType_Spec, 1);
225     if (spec == NULL) {
226         PyErr_NoMemory();
227         goto finally;
228     }
229     name = PyMem_New(char, sizeof(NAME));
230     if (name == NULL) {
231         PyErr_NoMemory();
232         goto finally;
233     }
234     memcpy(name, NAME, sizeof(NAME));
235 
236     doc = PyMem_New(char, sizeof(DOC));
237     if (doc == NULL) {
238         PyErr_NoMemory();
239         goto finally;
240     }
241     memcpy(doc, DOC, sizeof(DOC));
242 
243     spec->name = name;
244     spec->basicsize = sizeof(PyObject);
245     spec->itemsize = 0;
246     spec->flags = Py_TPFLAGS_DEFAULT;
247     slots = PyMem_New(PyType_Slot, 3);
248     if (slots == NULL) {
249         PyErr_NoMemory();
250         goto finally;
251     }
252     slots[0].slot = Py_tp_str;
253     slots[0].pfunc = simple_str;
254     slots[1].slot = Py_tp_doc;
255     slots[1].pfunc = doc;
256     slots[2].slot = 0;
257     slots[2].pfunc = NULL;
258     spec->slots = slots;
259 
260     /* create the class */
261 
262     class = PyType_FromSpec(spec);
263     if (class == NULL) {
264         goto finally;
265     }
266 
267     /* deallocate the spec (and all contents) */
268 
269     // (Explicitly overwrite memory before freeing,
270     // so bugs show themselves even without the debug allocator's help.)
271     memset(spec, 0xdd, sizeof(PyType_Spec));
272     PyMem_Del(spec);
273     spec = NULL;
274     memset(name, 0xdd, sizeof(NAME));
275     PyMem_Del(name);
276     name = NULL;
277     memset(doc, 0xdd, sizeof(DOC));
278     PyMem_Del(doc);
279     doc = NULL;
280     memset(slots, 0xdd, 3 * sizeof(PyType_Slot));
281     PyMem_Del(slots);
282     slots = NULL;
283 
284     /* check that everything works */
285 
286     PyTypeObject *class_tp = (PyTypeObject *)class;
287     PyHeapTypeObject *class_ht = (PyHeapTypeObject *)class;
288     assert(strcmp(class_tp->tp_name, "testcapi._Test") == 0);
289     assert(strcmp(PyUnicode_AsUTF8(class_ht->ht_name), "_Test") == 0);
290     assert(strcmp(PyUnicode_AsUTF8(class_ht->ht_qualname), "_Test") == 0);
291     assert(strcmp(class_tp->tp_doc, "a test class") == 0);
292 
293     // call and check __str__
294     instance = PyObject_CallNoArgs(class);
295     if (instance == NULL) {
296         goto finally;
297     }
298     obj = PyObject_Str(instance);
299     if (obj == NULL) {
300         goto finally;
301     }
302     assert(strcmp(PyUnicode_AsUTF8(obj), "<test>") == 0);
303     Py_CLEAR(obj);
304 
305     result = Py_NewRef(Py_None);
306   finally:
307     PyMem_Del(spec);
308     PyMem_Del(name);
309     PyMem_Del(doc);
310     PyMem_Del(slots);
311     Py_XDECREF(class);
312     Py_XDECREF(instance);
313     Py_XDECREF(obj);
314     return result;
315 }
316 
317 PyType_Slot repeated_doc_slots[] = {
318     {Py_tp_doc, "A class used for tests·"},
319     {Py_tp_doc, "A class used for tests"},
320     {0, 0},
321 };
322 
323 PyType_Spec repeated_doc_slots_spec = {
324     .name = "RepeatedDocSlotClass",
325     .basicsize = sizeof(PyObject),
326     .slots = repeated_doc_slots,
327 };
328 
329 typedef struct {
330     PyObject_HEAD
331     int data;
332 } HeapCTypeWithDataObject;
333 
334 
335 static struct PyMemberDef members_to_repeat[] = {
336     {"Py_T_INT", Py_T_INT, offsetof(HeapCTypeWithDataObject, data), 0, NULL},
337     {NULL}
338 };
339 
340 PyType_Slot repeated_members_slots[] = {
341     {Py_tp_members, members_to_repeat},
342     {Py_tp_members, members_to_repeat},
343     {0, 0},
344 };
345 
346 PyType_Spec repeated_members_slots_spec = {
347     .name = "RepeatedMembersSlotClass",
348     .basicsize = sizeof(HeapCTypeWithDataObject),
349     .slots = repeated_members_slots,
350 };
351 
352 static PyObject *
create_type_from_repeated_slots(PyObject * self,PyObject * variant_obj)353 create_type_from_repeated_slots(PyObject *self, PyObject *variant_obj)
354 {
355     PyObject *class = NULL;
356     int variant = PyLong_AsLong(variant_obj);
357     if (PyErr_Occurred()) {
358         return NULL;
359     }
360     switch (variant) {
361         case 0:
362             class = PyType_FromSpec(&repeated_doc_slots_spec);
363             break;
364         case 1:
365             class = PyType_FromSpec(&repeated_members_slots_spec);
366             break;
367         default:
368             PyErr_SetString(PyExc_ValueError, "bad test variant");
369             break;
370         }
371     return class;
372 }
373 
374 
375 static PyObject *
make_immutable_type_with_base(PyObject * self,PyObject * base)376 make_immutable_type_with_base(PyObject *self, PyObject *base)
377 {
378     assert(PyType_Check(base));
379     PyType_Spec ImmutableSubclass_spec = {
380         .name = "ImmutableSubclass",
381         .basicsize = (int)((PyTypeObject*)base)->tp_basicsize,
382         .slots = empty_type_slots,
383         .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_IMMUTABLETYPE,
384     };
385     return PyType_FromSpecWithBases(&ImmutableSubclass_spec, base);
386 }
387 
388 static PyObject *
make_type_with_base(PyObject * self,PyObject * base)389 make_type_with_base(PyObject *self, PyObject *base)
390 {
391     assert(PyType_Check(base));
392     PyType_Spec ImmutableSubclass_spec = {
393         .name = "_testcapi.Subclass",
394         .basicsize = (int)((PyTypeObject*)base)->tp_basicsize,
395         .slots = empty_type_slots,
396         .flags = Py_TPFLAGS_DEFAULT,
397     };
398     return PyType_FromSpecWithBases(&ImmutableSubclass_spec, base);
399 }
400 
401 
402 static PyObject *
pyobject_getitemdata(PyObject * self,PyObject * o)403 pyobject_getitemdata(PyObject *self, PyObject *o)
404 {
405     void *pointer = PyObject_GetItemData(o);
406     if (pointer == NULL) {
407         return NULL;
408     }
409     return PyLong_FromVoidPtr(pointer);
410 }
411 
412 
413 static PyMethodDef TestMethods[] = {
414     {"pytype_fromspec_meta",    pytype_fromspec_meta,            METH_O},
415     {"test_type_from_ephemeral_spec", test_type_from_ephemeral_spec, METH_NOARGS},
416     {"create_type_from_repeated_slots",
417         create_type_from_repeated_slots, METH_O},
418     {"test_from_spec_metatype_inheritance", test_from_spec_metatype_inheritance,
419      METH_NOARGS},
420     {"test_from_spec_invalid_metatype_inheritance",
421      test_from_spec_invalid_metatype_inheritance,
422      METH_NOARGS},
423     {"make_immutable_type_with_base", make_immutable_type_with_base, METH_O},
424     {"make_type_with_base", make_type_with_base, METH_O},
425     {"pyobject_getitemdata", pyobject_getitemdata, METH_O},
426     {NULL},
427 };
428 
429 
430 PyDoc_STRVAR(heapdocctype__doc__,
431 "HeapDocCType(arg1, arg2)\n"
432 "--\n"
433 "\n"
434 "somedoc");
435 
436 typedef struct {
437     PyObject_HEAD
438 } HeapDocCTypeObject;
439 
440 static PyType_Slot HeapDocCType_slots[] = {
441     {Py_tp_doc, (char*)heapdocctype__doc__},
442     {0},
443 };
444 
445 static PyType_Spec HeapDocCType_spec = {
446     "_testcapi.HeapDocCType",
447     sizeof(HeapDocCTypeObject),
448     0,
449     Py_TPFLAGS_DEFAULT,
450     HeapDocCType_slots
451 };
452 
453 typedef struct {
454     PyObject_HEAD
455 } NullTpDocTypeObject;
456 
457 static PyType_Slot NullTpDocType_slots[] = {
458     {Py_tp_doc, NULL},
459     {0, 0},
460 };
461 
462 static PyType_Spec NullTpDocType_spec = {
463     "_testcapi.NullTpDocType",
464     sizeof(NullTpDocTypeObject),
465     0,
466     Py_TPFLAGS_DEFAULT,
467     NullTpDocType_slots
468 };
469 
470 
471 PyDoc_STRVAR(heapgctype__doc__,
472 "A heap type with GC, and with overridden dealloc.\n\n"
473 "The 'value' attribute is set to 10 in __init__.");
474 
475 typedef struct {
476     PyObject_HEAD
477     int value;
478 } HeapCTypeObject;
479 
480 static struct PyMemberDef heapctype_members[] = {
481     {"value", Py_T_INT, offsetof(HeapCTypeObject, value)},
482     {NULL} /* Sentinel */
483 };
484 
485 static int
heapctype_init(PyObject * self,PyObject * args,PyObject * kwargs)486 heapctype_init(PyObject *self, PyObject *args, PyObject *kwargs)
487 {
488     ((HeapCTypeObject *)self)->value = 10;
489     return 0;
490 }
491 
492 static int
heapgcctype_traverse(HeapCTypeObject * self,visitproc visit,void * arg)493 heapgcctype_traverse(HeapCTypeObject *self, visitproc visit, void *arg)
494 {
495     Py_VISIT(Py_TYPE(self));
496     return 0;
497 }
498 
499 static void
heapgcctype_dealloc(HeapCTypeObject * self)500 heapgcctype_dealloc(HeapCTypeObject *self)
501 {
502     PyTypeObject *tp = Py_TYPE(self);
503     PyObject_GC_UnTrack(self);
504     PyObject_GC_Del(self);
505     Py_DECREF(tp);
506 }
507 
508 static PyType_Slot HeapGcCType_slots[] = {
509     {Py_tp_init, heapctype_init},
510     {Py_tp_members, heapctype_members},
511     {Py_tp_dealloc, heapgcctype_dealloc},
512     {Py_tp_traverse, heapgcctype_traverse},
513     {Py_tp_doc, (char*)heapgctype__doc__},
514     {0, 0},
515 };
516 
517 static PyType_Spec HeapGcCType_spec = {
518     "_testcapi.HeapGcCType",
519     sizeof(HeapCTypeObject),
520     0,
521     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC,
522     HeapGcCType_slots
523 };
524 
525 PyDoc_STRVAR(heapctype__doc__,
526 "A heap type without GC, but with overridden dealloc.\n\n"
527 "The 'value' attribute is set to 10 in __init__.");
528 
529 static void
heapctype_dealloc(HeapCTypeObject * self)530 heapctype_dealloc(HeapCTypeObject *self)
531 {
532     PyTypeObject *tp = Py_TYPE(self);
533     PyObject_Free(self);
534     Py_DECREF(tp);
535 }
536 
537 static PyType_Slot HeapCType_slots[] = {
538     {Py_tp_init, heapctype_init},
539     {Py_tp_members, heapctype_members},
540     {Py_tp_dealloc, heapctype_dealloc},
541     {Py_tp_doc, (char*)heapctype__doc__},
542     {0, 0},
543 };
544 
545 static PyType_Spec HeapCType_spec = {
546     "_testcapi.HeapCType",
547     sizeof(HeapCTypeObject),
548     0,
549     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
550     HeapCType_slots
551 };
552 
553 PyDoc_STRVAR(heapctypesubclass__doc__,
554 "Subclass of HeapCType, without GC.\n\n"
555 "__init__ sets the 'value' attribute to 10 and 'value2' to 20.");
556 
557 typedef struct {
558     HeapCTypeObject base;
559     int value2;
560 } HeapCTypeSubclassObject;
561 
562 static int
heapctypesubclass_init(PyObject * self,PyObject * args,PyObject * kwargs)563 heapctypesubclass_init(PyObject *self, PyObject *args, PyObject *kwargs)
564 {
565     /* Call __init__ of the superclass */
566     if (heapctype_init(self, args, kwargs) < 0) {
567         return -1;
568     }
569     /* Initialize additional element */
570     ((HeapCTypeSubclassObject *)self)->value2 = 20;
571     return 0;
572 }
573 
574 static struct PyMemberDef heapctypesubclass_members[] = {
575     {"value2", Py_T_INT, offsetof(HeapCTypeSubclassObject, value2)},
576     {NULL} /* Sentinel */
577 };
578 
579 static PyType_Slot HeapCTypeSubclass_slots[] = {
580     {Py_tp_init, heapctypesubclass_init},
581     {Py_tp_members, heapctypesubclass_members},
582     {Py_tp_doc, (char*)heapctypesubclass__doc__},
583     {0, 0},
584 };
585 
586 static PyType_Spec HeapCTypeSubclass_spec = {
587     "_testcapi.HeapCTypeSubclass",
588     sizeof(HeapCTypeSubclassObject),
589     0,
590     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
591     HeapCTypeSubclass_slots
592 };
593 
594 PyDoc_STRVAR(heapctypewithbuffer__doc__,
595 "Heap type with buffer support.\n\n"
596 "The buffer is set to [b'1', b'2', b'3', b'4']");
597 
598 typedef struct {
599     HeapCTypeObject base;
600     char buffer[4];
601 } HeapCTypeWithBufferObject;
602 
603 static int
heapctypewithbuffer_getbuffer(HeapCTypeWithBufferObject * self,Py_buffer * view,int flags)604 heapctypewithbuffer_getbuffer(HeapCTypeWithBufferObject *self, Py_buffer *view, int flags)
605 {
606     self->buffer[0] = '1';
607     self->buffer[1] = '2';
608     self->buffer[2] = '3';
609     self->buffer[3] = '4';
610     return PyBuffer_FillInfo(
611         view, (PyObject*)self, (void *)self->buffer, 4, 1, flags);
612 }
613 
614 static void
heapctypewithbuffer_releasebuffer(HeapCTypeWithBufferObject * self,Py_buffer * view)615 heapctypewithbuffer_releasebuffer(HeapCTypeWithBufferObject *self, Py_buffer *view)
616 {
617     assert(view->obj == (void*) self);
618 }
619 
620 static PyType_Slot HeapCTypeWithBuffer_slots[] = {
621     {Py_bf_getbuffer, heapctypewithbuffer_getbuffer},
622     {Py_bf_releasebuffer, heapctypewithbuffer_releasebuffer},
623     {Py_tp_doc, (char*)heapctypewithbuffer__doc__},
624     {0, 0},
625 };
626 
627 static PyType_Spec HeapCTypeWithBuffer_spec = {
628     "_testcapi.HeapCTypeWithBuffer",
629     sizeof(HeapCTypeWithBufferObject),
630     0,
631     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
632     HeapCTypeWithBuffer_slots
633 };
634 
635 PyDoc_STRVAR(heapctypesubclasswithfinalizer__doc__,
636 "Subclass of HeapCType with a finalizer that reassigns __class__.\n\n"
637 "__class__ is set to plain HeapCTypeSubclass during finalization.\n"
638 "__init__ sets the 'value' attribute to 10 and 'value2' to 20.");
639 
640 static int
heapctypesubclasswithfinalizer_init(PyObject * self,PyObject * args,PyObject * kwargs)641 heapctypesubclasswithfinalizer_init(PyObject *self, PyObject *args, PyObject *kwargs)
642 {
643     PyTypeObject *base = (PyTypeObject *)PyType_GetSlot(Py_TYPE(self), Py_tp_base);
644     initproc base_init = PyType_GetSlot(base, Py_tp_init);
645     base_init(self, args, kwargs);
646     return 0;
647 }
648 
649 static void
heapctypesubclasswithfinalizer_finalize(PyObject * self)650 heapctypesubclasswithfinalizer_finalize(PyObject *self)
651 {
652     PyObject *oldtype = NULL, *newtype = NULL, *refcnt = NULL;
653 
654     /* Save the current exception, if any. */
655     PyObject *exc = PyErr_GetRaisedException();
656 
657     if (_testcapimodule == NULL) {
658         goto cleanup_finalize;
659     }
660     PyObject *m = PyState_FindModule(_testcapimodule);
661     if (m == NULL) {
662         goto cleanup_finalize;
663     }
664     oldtype = PyObject_GetAttrString(m, "HeapCTypeSubclassWithFinalizer");
665     if (oldtype == NULL) {
666         goto cleanup_finalize;
667     }
668     newtype = PyObject_GetAttrString(m, "HeapCTypeSubclass");
669     if (newtype == NULL) {
670         goto cleanup_finalize;
671     }
672 
673     if (PyObject_SetAttrString(self, "__class__", newtype) < 0) {
674         goto cleanup_finalize;
675     }
676     refcnt = PyLong_FromSsize_t(Py_REFCNT(oldtype));
677     if (refcnt == NULL) {
678         goto cleanup_finalize;
679     }
680     if (PyObject_SetAttrString(oldtype, "refcnt_in_del", refcnt) < 0) {
681         goto cleanup_finalize;
682     }
683     Py_DECREF(refcnt);
684     refcnt = PyLong_FromSsize_t(Py_REFCNT(newtype));
685     if (refcnt == NULL) {
686         goto cleanup_finalize;
687     }
688     if (PyObject_SetAttrString(newtype, "refcnt_in_del", refcnt) < 0) {
689         goto cleanup_finalize;
690     }
691 
692 cleanup_finalize:
693     Py_XDECREF(oldtype);
694     Py_XDECREF(newtype);
695     Py_XDECREF(refcnt);
696 
697     /* Restore the saved exception. */
698     PyErr_SetRaisedException(exc);
699 }
700 
701 static PyType_Slot HeapCTypeSubclassWithFinalizer_slots[] = {
702     {Py_tp_init, heapctypesubclasswithfinalizer_init},
703     {Py_tp_members, heapctypesubclass_members},
704     {Py_tp_finalize, heapctypesubclasswithfinalizer_finalize},
705     {Py_tp_doc, (char*)heapctypesubclasswithfinalizer__doc__},
706     {0, 0},
707 };
708 
709 static PyType_Spec HeapCTypeSubclassWithFinalizer_spec = {
710     "_testcapi.HeapCTypeSubclassWithFinalizer",
711     sizeof(HeapCTypeSubclassObject),
712     0,
713     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_FINALIZE,
714     HeapCTypeSubclassWithFinalizer_slots
715 };
716 
717 static PyType_Slot HeapCTypeMetaclass_slots[] = {
718     {0},
719 };
720 
721 static PyType_Spec HeapCTypeMetaclass_spec = {
722     "_testcapi.HeapCTypeMetaclass",
723     sizeof(PyHeapTypeObject),
724     sizeof(PyMemberDef),
725     Py_TPFLAGS_DEFAULT,
726     HeapCTypeMetaclass_slots
727 };
728 
729 static PyObject *
heap_ctype_metaclass_custom_tp_new(PyTypeObject * tp,PyObject * args,PyObject * kwargs)730 heap_ctype_metaclass_custom_tp_new(PyTypeObject *tp, PyObject *args, PyObject *kwargs)
731 {
732     return PyType_Type.tp_new(tp, args, kwargs);
733 }
734 
735 static PyType_Slot HeapCTypeMetaclassCustomNew_slots[] = {
736     { Py_tp_new, heap_ctype_metaclass_custom_tp_new },
737     {0},
738 };
739 
740 static PyType_Spec HeapCTypeMetaclassCustomNew_spec = {
741     "_testcapi.HeapCTypeMetaclassCustomNew",
742     sizeof(PyHeapTypeObject),
743     sizeof(PyMemberDef),
744     Py_TPFLAGS_DEFAULT,
745     HeapCTypeMetaclassCustomNew_slots
746 };
747 
748 static PyType_Spec HeapCTypeMetaclassNullNew_spec = {
749     .name = "_testcapi.HeapCTypeMetaclassNullNew",
750     .flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_DISALLOW_INSTANTIATION,
751     .slots = empty_type_slots
752 };
753 
754 
755 typedef struct {
756     PyObject_HEAD
757     PyObject *dict;
758 } HeapCTypeWithDictObject;
759 
760 static void
heapctypewithdict_dealloc(HeapCTypeWithDictObject * self)761 heapctypewithdict_dealloc(HeapCTypeWithDictObject* self)
762 {
763 
764     PyTypeObject *tp = Py_TYPE(self);
765     Py_XDECREF(self->dict);
766     PyObject_Free(self);
767     Py_DECREF(tp);
768 }
769 
770 static PyGetSetDef heapctypewithdict_getsetlist[] = {
771     {"__dict__", PyObject_GenericGetDict, PyObject_GenericSetDict},
772     {NULL} /* Sentinel */
773 };
774 
775 static struct PyMemberDef heapctypewithdict_members[] = {
776     {"dictobj", _Py_T_OBJECT, offsetof(HeapCTypeWithDictObject, dict)},
777     {"__dictoffset__", Py_T_PYSSIZET, offsetof(HeapCTypeWithDictObject, dict), Py_READONLY},
778     {NULL} /* Sentinel */
779 };
780 
781 static PyType_Slot HeapCTypeWithDict_slots[] = {
782     {Py_tp_members, heapctypewithdict_members},
783     {Py_tp_getset, heapctypewithdict_getsetlist},
784     {Py_tp_dealloc, heapctypewithdict_dealloc},
785     {0, 0},
786 };
787 
788 static PyType_Spec HeapCTypeWithDict_spec = {
789     "_testcapi.HeapCTypeWithDict",
790     sizeof(HeapCTypeWithDictObject),
791     0,
792     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
793     HeapCTypeWithDict_slots
794 };
795 
796 static PyType_Spec HeapCTypeWithDict2_spec = {
797     "_testcapi.HeapCTypeWithDict2",
798     sizeof(HeapCTypeWithDictObject),
799     0,
800     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
801     HeapCTypeWithDict_slots
802 };
803 
804 static int
heapmanaged_traverse(HeapCTypeObject * self,visitproc visit,void * arg)805 heapmanaged_traverse(HeapCTypeObject *self, visitproc visit, void *arg)
806 {
807     Py_VISIT(Py_TYPE(self));
808     return PyObject_VisitManagedDict((PyObject *)self, visit, arg);
809 }
810 
811 static int
heapmanaged_clear(HeapCTypeObject * self)812 heapmanaged_clear(HeapCTypeObject *self)
813 {
814     PyObject_ClearManagedDict((PyObject *)self);
815     return 0;
816 }
817 
818 static void
heapmanaged_dealloc(HeapCTypeObject * self)819 heapmanaged_dealloc(HeapCTypeObject *self)
820 {
821     PyTypeObject *tp = Py_TYPE(self);
822     PyObject_ClearManagedDict((PyObject *)self);
823     PyObject_GC_UnTrack(self);
824     PyObject_GC_Del(self);
825     Py_DECREF(tp);
826 }
827 
828 static PyType_Slot HeapCTypeWithManagedDict_slots[] = {
829     {Py_tp_traverse, heapmanaged_traverse},
830     {Py_tp_getset, heapctypewithdict_getsetlist},
831     {Py_tp_clear, heapmanaged_clear},
832     {Py_tp_dealloc, heapmanaged_dealloc},
833     {0, 0},
834 };
835 
836 static PyType_Spec  HeapCTypeWithManagedDict_spec = {
837     "_testcapi.HeapCTypeWithManagedDict",
838     sizeof(PyObject),
839     0,
840     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_DICT,
841     HeapCTypeWithManagedDict_slots
842 };
843 
844 static void
heapctypewithmanagedweakref_dealloc(PyObject * self)845 heapctypewithmanagedweakref_dealloc(PyObject* self)
846 {
847 
848     PyTypeObject *tp = Py_TYPE(self);
849     PyObject_ClearWeakRefs(self);
850     PyObject_GC_UnTrack(self);
851     PyObject_GC_Del(self);
852     Py_DECREF(tp);
853 }
854 
855 static PyType_Slot HeapCTypeWithManagedWeakref_slots[] = {
856     {Py_tp_traverse, heapgcctype_traverse},
857     {Py_tp_getset, heapctypewithdict_getsetlist},
858     {Py_tp_dealloc, heapctypewithmanagedweakref_dealloc},
859     {0, 0},
860 };
861 
862 static PyType_Spec  HeapCTypeWithManagedWeakref_spec = {
863     "_testcapi.HeapCTypeWithManagedWeakref",
864     sizeof(PyObject),
865     0,
866     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_MANAGED_WEAKREF,
867     HeapCTypeWithManagedWeakref_slots
868 };
869 
870 static struct PyMemberDef heapctypewithnegativedict_members[] = {
871     {"dictobj", _Py_T_OBJECT, offsetof(HeapCTypeWithDictObject, dict)},
872     {"__dictoffset__", Py_T_PYSSIZET, -(Py_ssize_t)sizeof(void*), Py_READONLY},
873     {NULL} /* Sentinel */
874 };
875 
876 static PyType_Slot HeapCTypeWithNegativeDict_slots[] = {
877     {Py_tp_members, heapctypewithnegativedict_members},
878     {Py_tp_getset, heapctypewithdict_getsetlist},
879     {Py_tp_dealloc, heapctypewithdict_dealloc},
880     {0, 0},
881 };
882 
883 static PyType_Spec HeapCTypeWithNegativeDict_spec = {
884     "_testcapi.HeapCTypeWithNegativeDict",
885     sizeof(HeapCTypeWithDictObject),
886     0,
887     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
888     HeapCTypeWithNegativeDict_slots
889 };
890 
891 typedef struct {
892     PyObject_HEAD
893     PyObject *weakreflist;
894 } HeapCTypeWithWeakrefObject;
895 
896 static struct PyMemberDef heapctypewithweakref_members[] = {
897     {"weakreflist", _Py_T_OBJECT, offsetof(HeapCTypeWithWeakrefObject, weakreflist)},
898     {"__weaklistoffset__", Py_T_PYSSIZET,
899       offsetof(HeapCTypeWithWeakrefObject, weakreflist), Py_READONLY},
900     {NULL} /* Sentinel */
901 };
902 
903 static void
heapctypewithweakref_dealloc(HeapCTypeWithWeakrefObject * self)904 heapctypewithweakref_dealloc(HeapCTypeWithWeakrefObject* self)
905 {
906 
907     PyTypeObject *tp = Py_TYPE(self);
908     if (self->weakreflist != NULL)
909         PyObject_ClearWeakRefs((PyObject *) self);
910     Py_XDECREF(self->weakreflist);
911     PyObject_Free(self);
912     Py_DECREF(tp);
913 }
914 
915 static PyType_Slot HeapCTypeWithWeakref_slots[] = {
916     {Py_tp_members, heapctypewithweakref_members},
917     {Py_tp_dealloc, heapctypewithweakref_dealloc},
918     {0, 0},
919 };
920 
921 static PyType_Spec HeapCTypeWithWeakref_spec = {
922     "_testcapi.HeapCTypeWithWeakref",
923     sizeof(HeapCTypeWithWeakrefObject),
924     0,
925     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
926     HeapCTypeWithWeakref_slots
927 };
928 
929 static PyType_Spec HeapCTypeWithWeakref2_spec = {
930     "_testcapi.HeapCTypeWithWeakref2",
931     sizeof(HeapCTypeWithWeakrefObject),
932     0,
933     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
934     HeapCTypeWithWeakref_slots
935 };
936 
937 PyDoc_STRVAR(heapctypesetattr__doc__,
938 "A heap type without GC, but with overridden __setattr__.\n\n"
939 "The 'value' attribute is set to 10 in __init__ and updated via attribute setting.");
940 
941 typedef struct {
942     PyObject_HEAD
943     long value;
944 } HeapCTypeSetattrObject;
945 
946 static struct PyMemberDef heapctypesetattr_members[] = {
947     {"pvalue", Py_T_LONG, offsetof(HeapCTypeSetattrObject, value)},
948     {NULL} /* Sentinel */
949 };
950 
951 static int
heapctypesetattr_init(PyObject * self,PyObject * args,PyObject * kwargs)952 heapctypesetattr_init(PyObject *self, PyObject *args, PyObject *kwargs)
953 {
954     ((HeapCTypeSetattrObject *)self)->value = 10;
955     return 0;
956 }
957 
958 static void
heapctypesetattr_dealloc(HeapCTypeSetattrObject * self)959 heapctypesetattr_dealloc(HeapCTypeSetattrObject *self)
960 {
961     PyTypeObject *tp = Py_TYPE(self);
962     PyObject_Free(self);
963     Py_DECREF(tp);
964 }
965 
966 static int
heapctypesetattr_setattro(HeapCTypeSetattrObject * self,PyObject * attr,PyObject * value)967 heapctypesetattr_setattro(HeapCTypeSetattrObject *self, PyObject *attr, PyObject *value)
968 {
969     PyObject *svalue = PyUnicode_FromString("value");
970     if (svalue == NULL)
971         return -1;
972     int eq = PyObject_RichCompareBool(svalue, attr, Py_EQ);
973     Py_DECREF(svalue);
974     if (eq < 0)
975         return -1;
976     if (!eq) {
977         return PyObject_GenericSetAttr((PyObject*) self, attr, value);
978     }
979     if (value == NULL) {
980         self->value = 0;
981         return 0;
982     }
983     PyObject *ivalue = PyNumber_Long(value);
984     if (ivalue == NULL)
985         return -1;
986     long v = PyLong_AsLong(ivalue);
987     Py_DECREF(ivalue);
988     if (v == -1 && PyErr_Occurred())
989         return -1;
990     self->value = v;
991     return 0;
992 }
993 
994 static PyType_Slot HeapCTypeSetattr_slots[] = {
995     {Py_tp_init, heapctypesetattr_init},
996     {Py_tp_members, heapctypesetattr_members},
997     {Py_tp_setattro, heapctypesetattr_setattro},
998     {Py_tp_dealloc, heapctypesetattr_dealloc},
999     {Py_tp_doc, (char*)heapctypesetattr__doc__},
1000     {0, 0},
1001 };
1002 
1003 static PyType_Spec HeapCTypeSetattr_spec = {
1004     "_testcapi.HeapCTypeSetattr",
1005     sizeof(HeapCTypeSetattrObject),
1006     0,
1007     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1008     HeapCTypeSetattr_slots
1009 };
1010 
1011 PyDoc_STRVAR(HeapCCollection_doc,
1012 "Tuple-like heap type that uses PyObject_GetItemData for items.");
1013 
1014 static PyObject*
HeapCCollection_new(PyTypeObject * subtype,PyObject * args,PyObject * kwds)1015 HeapCCollection_new(PyTypeObject *subtype, PyObject *args, PyObject *kwds)
1016 {
1017     PyObject *self = NULL;
1018     PyObject *result = NULL;
1019 
1020     Py_ssize_t size = PyTuple_GET_SIZE(args);
1021     self = subtype->tp_alloc(subtype, size);
1022     if (!self) {
1023         goto finally;
1024     }
1025     PyObject **data = PyObject_GetItemData(self);
1026     if (!data) {
1027         goto finally;
1028     }
1029 
1030     for (Py_ssize_t i = 0; i < size; i++) {
1031         data[i] = Py_NewRef(PyTuple_GET_ITEM(args, i));
1032     }
1033 
1034     result = self;
1035     self = NULL;
1036   finally:
1037     Py_XDECREF(self);
1038     return result;
1039 }
1040 
1041 static Py_ssize_t
HeapCCollection_length(PyVarObject * self)1042 HeapCCollection_length(PyVarObject *self)
1043 {
1044     return Py_SIZE(self);
1045 }
1046 
1047 static PyObject*
HeapCCollection_item(PyObject * self,Py_ssize_t i)1048 HeapCCollection_item(PyObject *self, Py_ssize_t i)
1049 {
1050     if (i < 0 || i >= Py_SIZE(self)) {
1051         return PyErr_Format(PyExc_IndexError, "index %zd out of range", i);
1052     }
1053     PyObject **data = PyObject_GetItemData(self);
1054     if (!data) {
1055         return NULL;
1056     }
1057     return Py_NewRef(data[i]);
1058 }
1059 
1060 static int
HeapCCollection_traverse(PyObject * self,visitproc visit,void * arg)1061 HeapCCollection_traverse(PyObject *self, visitproc visit, void *arg)
1062 {
1063     PyObject **data = PyObject_GetItemData(self);
1064     if (!data) {
1065         return -1;
1066     }
1067     for (Py_ssize_t i = 0; i < Py_SIZE(self); i++) {
1068         Py_VISIT(data[i]);
1069     }
1070     return 0;
1071 }
1072 
1073 static int
HeapCCollection_clear(PyObject * self)1074 HeapCCollection_clear(PyObject *self)
1075 {
1076     PyObject **data = PyObject_GetItemData(self);
1077     if (!data) {
1078         return -1;
1079     }
1080     Py_ssize_t size = Py_SIZE(self);
1081     Py_SET_SIZE(self, 0);
1082     for (Py_ssize_t i = 0; i < size; i++) {
1083         Py_CLEAR(data[i]);
1084     }
1085     return 0;
1086 }
1087 
1088 static void
HeapCCollection_dealloc(PyObject * self)1089 HeapCCollection_dealloc(PyObject *self)
1090 {
1091     PyTypeObject *tp = Py_TYPE(self);
1092     HeapCCollection_clear(self);
1093     PyObject_GC_UnTrack(self);
1094     tp->tp_free(self);
1095     Py_DECREF(tp);
1096 }
1097 
1098 static PyType_Slot HeapCCollection_slots[] = {
1099     {Py_tp_new, HeapCCollection_new},
1100     {Py_sq_length, HeapCCollection_length},
1101     {Py_sq_item, HeapCCollection_item},
1102     {Py_tp_traverse, HeapCCollection_traverse},
1103     {Py_tp_clear, HeapCCollection_clear},
1104     {Py_tp_dealloc, HeapCCollection_dealloc},
1105     {Py_tp_doc, (void *)HeapCCollection_doc},
1106     {0, 0},
1107 };
1108 
1109 static PyType_Spec HeapCCollection_spec = {
1110     .name = "_testcapi.HeapCCollection",
1111     .basicsize = sizeof(PyVarObject),
1112     .itemsize = sizeof(PyObject*),
1113     .flags = (Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE |
1114               Py_TPFLAGS_HAVE_GC | Py_TPFLAGS_ITEMS_AT_END),
1115     .slots = HeapCCollection_slots,
1116 };
1117 
1118 int
_PyTestCapi_Init_Heaptype(PyObject * m)1119 _PyTestCapi_Init_Heaptype(PyObject *m) {
1120     _testcapimodule = PyModule_GetDef(m);
1121 
1122     if (PyModule_AddFunctions(m, TestMethods) < 0) {
1123         return -1;
1124     }
1125 
1126 #define ADD(name, value) do { \
1127         if (PyModule_Add(m, name, value) < 0) { \
1128             return -1; \
1129         } \
1130     } while (0)
1131 
1132     PyObject *HeapDocCType = PyType_FromSpec(&HeapDocCType_spec);
1133     ADD("HeapDocCType", HeapDocCType);
1134 
1135     /* bpo-41832: Add a new type to test PyType_FromSpec()
1136        now can accept a NULL tp_doc slot. */
1137     PyObject *NullTpDocType = PyType_FromSpec(&NullTpDocType_spec);
1138     ADD("NullTpDocType", NullTpDocType);
1139 
1140     PyObject *HeapGcCType = PyType_FromSpec(&HeapGcCType_spec);
1141     ADD("HeapGcCType", HeapGcCType);
1142 
1143     PyObject *HeapCType = PyType_FromSpec(&HeapCType_spec);
1144     if (HeapCType == NULL) {
1145         return -1;
1146     }
1147     PyObject *subclass_bases = PyTuple_Pack(1, HeapCType);
1148     Py_DECREF(HeapCType);
1149     if (subclass_bases == NULL) {
1150         return -1;
1151     }
1152     PyObject *HeapCTypeSubclass = PyType_FromSpecWithBases(&HeapCTypeSubclass_spec, subclass_bases);
1153     Py_DECREF(subclass_bases);
1154     ADD("HeapCTypeSubclass", HeapCTypeSubclass);
1155 
1156     PyObject *HeapCTypeWithDict = PyType_FromSpec(&HeapCTypeWithDict_spec);
1157     ADD("HeapCTypeWithDict", HeapCTypeWithDict);
1158 
1159     PyObject *HeapCTypeWithDict2 = PyType_FromSpec(&HeapCTypeWithDict2_spec);
1160     ADD("HeapCTypeWithDict2", HeapCTypeWithDict2);
1161 
1162     PyObject *HeapCTypeWithNegativeDict = PyType_FromSpec(&HeapCTypeWithNegativeDict_spec);
1163     ADD("HeapCTypeWithNegativeDict", HeapCTypeWithNegativeDict);
1164 
1165     PyObject *HeapCTypeWithManagedDict = PyType_FromSpec(&HeapCTypeWithManagedDict_spec);
1166     ADD("HeapCTypeWithManagedDict", HeapCTypeWithManagedDict);
1167 
1168     PyObject *HeapCTypeWithManagedWeakref = PyType_FromSpec(&HeapCTypeWithManagedWeakref_spec);
1169     ADD("HeapCTypeWithManagedWeakref", HeapCTypeWithManagedWeakref);
1170 
1171     PyObject *HeapCTypeWithWeakref = PyType_FromSpec(&HeapCTypeWithWeakref_spec);
1172     ADD("HeapCTypeWithWeakref", HeapCTypeWithWeakref);
1173 
1174     PyObject *HeapCTypeWithWeakref2 = PyType_FromSpec(&HeapCTypeWithWeakref2_spec);
1175     ADD("HeapCTypeWithWeakref2", HeapCTypeWithWeakref2);
1176 
1177     PyObject *HeapCTypeWithBuffer = PyType_FromSpec(&HeapCTypeWithBuffer_spec);
1178     ADD("HeapCTypeWithBuffer", HeapCTypeWithBuffer);
1179 
1180     PyObject *HeapCTypeSetattr = PyType_FromSpec(&HeapCTypeSetattr_spec);
1181     ADD("HeapCTypeSetattr", HeapCTypeSetattr);
1182 
1183     PyObject *subclass_with_finalizer_bases = PyTuple_Pack(1, HeapCTypeSubclass);
1184     if (subclass_with_finalizer_bases == NULL) {
1185         return -1;
1186     }
1187     PyObject *HeapCTypeSubclassWithFinalizer = PyType_FromSpecWithBases(
1188         &HeapCTypeSubclassWithFinalizer_spec, subclass_with_finalizer_bases);
1189     Py_DECREF(subclass_with_finalizer_bases);
1190     ADD("HeapCTypeSubclassWithFinalizer", HeapCTypeSubclassWithFinalizer);
1191 
1192     PyObject *HeapCTypeMetaclass = PyType_FromMetaclass(
1193         &PyType_Type, m, &HeapCTypeMetaclass_spec, (PyObject *) &PyType_Type);
1194     ADD("HeapCTypeMetaclass", HeapCTypeMetaclass);
1195 
1196     PyObject *HeapCTypeMetaclassCustomNew = PyType_FromMetaclass(
1197         &PyType_Type, m, &HeapCTypeMetaclassCustomNew_spec, (PyObject *) &PyType_Type);
1198     ADD("HeapCTypeMetaclassCustomNew", HeapCTypeMetaclassCustomNew);
1199 
1200     PyObject *HeapCTypeMetaclassNullNew = PyType_FromMetaclass(
1201         &PyType_Type, m, &HeapCTypeMetaclassNullNew_spec, (PyObject *) &PyType_Type);
1202     ADD("HeapCTypeMetaclassNullNew", HeapCTypeMetaclassNullNew);
1203 
1204     PyObject *HeapCCollection = PyType_FromMetaclass(
1205         NULL, m, &HeapCCollection_spec, NULL);
1206     if (HeapCCollection == NULL) {
1207         return -1;
1208     }
1209     int rc = PyModule_AddType(m, (PyTypeObject *)HeapCCollection);
1210     Py_DECREF(HeapCCollection);
1211     if (rc < 0) {
1212         return -1;
1213     }
1214 
1215     return 0;
1216 }
1217