Panda3D
Loading...
Searching...
No Matches
py_wrappers.cxx
Go to the documentation of this file.
1/**
2 * @file py_wrappers.cxx
3 * @author rdb
4 * @date 2017-11-26
5 */
6
7#include "py_wrappers.h"
8
9#ifdef HAVE_PYTHON
10
11#if PY_VERSION_HEX >= 0x03040000
12#define _COLLECTIONS_ABC "_collections_abc"
13#elif PY_VERSION_HEX >= 0x03030000
14#define _COLLECTIONS_ABC "collections.abc"
15#else
16#define _COLLECTIONS_ABC "_abcoll"
17#endif
18
19static void _register_collection(PyTypeObject *type, const char *abc) {
20 PyObject *sys_modules = PyImport_GetModuleDict();
21 if (sys_modules != nullptr) {
22 PyObject *module = PyDict_GetItemString(sys_modules, _COLLECTIONS_ABC);
23 if (module != nullptr) {
24 PyObject *dict = PyModule_GetDict(module);
25 if (module != nullptr) {
26#if PY_MAJOR_VERSION >= 3
27 static PyObject *register_str = PyUnicode_InternFromString("register");
28#else
29 static PyObject *register_str = PyString_InternFromString("register");
30#endif
31 PyObject *sequence = PyDict_GetItemString(dict, abc);
32 if (sequence != nullptr) {
33 if (PyObject_CallMethodObjArgs(sequence, register_str, (PyObject *)type, nullptr) == nullptr) {
34 PyErr_Print();
35 }
36 }
37 }
38 }
39 }
40}
41
42/**
43 * These classes are returned from properties that require a subscript
44 * interface, ie. something.children[i] = 3.
45 */
46static void Dtool_WrapperBase_dealloc(PyObject *self) {
47 Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
48 nassertv(wrap);
49 Py_XDECREF(wrap->_self);
50 Py_TYPE(self)->tp_free(self);
51}
52
53static PyObject *Dtool_WrapperBase_repr(PyObject *self) {
54 Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
55 nassertr(wrap, nullptr);
56
57 PyObject *repr = PyObject_Repr(wrap->_self);
58 PyObject *result;
59#if PY_MAJOR_VERSION >= 3
60 result = PyUnicode_FromFormat("<%s[] of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
61#else
62 result = PyString_FromFormat("<%s[] of %s>", wrap->_name, PyString_AS_STRING(repr));
63#endif
64 Py_DECREF(repr);
65 return result;
66}
67
68static PyObject *Dtool_SequenceWrapper_repr(PyObject *self) {
69 Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
70 nassertr(wrap, nullptr);
71
72 Py_ssize_t len = -1;
73 if (wrap->_len_func != nullptr) {
74 len = wrap->_len_func(wrap->_base._self);
75 }
76
77 if (len < 0) {
78 PyErr_Restore(nullptr, nullptr, nullptr);
79 return Dtool_WrapperBase_repr(self);
80 }
81
82 PyObject *repr = PyObject_Repr(wrap->_base._self);
83 PyObject *result;
84#if PY_MAJOR_VERSION >= 3
85 result = PyUnicode_FromFormat("<%s[%zd] of %s>", wrap->_base._name, len, PyUnicode_AsUTF8(repr));
86#else
87 result = PyString_FromFormat("<%s[%zd] of %s>", wrap->_base._name, len, PyString_AS_STRING(repr));
88#endif
89 Py_DECREF(repr);
90 return result;
91}
92
93static Py_ssize_t Dtool_SequenceWrapper_length(PyObject *self) {
94 Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
95 nassertr(wrap, -1);
96 if (wrap->_len_func != nullptr) {
97 return wrap->_len_func(wrap->_base._self);
98 } else {
99 Dtool_Raise_TypeError("property does not support len()");
100 return -1;
101 }
102}
103
104static PyObject *Dtool_SequenceWrapper_getitem(PyObject *self, Py_ssize_t index) {
105 Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
106 nassertr(wrap, nullptr);
107 nassertr(wrap->_getitem_func, nullptr);
108 return wrap->_getitem_func(wrap->_base._self, index);
109}
110
111/**
112 * Implementation of (x in property)
113 */
114static int Dtool_SequenceWrapper_contains(PyObject *self, PyObject *value) {
115 Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
116 nassertr(wrap, -1);
117 nassertr(wrap->_len_func, -1);
118 nassertr(wrap->_getitem_func, -1);
119
120 Py_ssize_t length = wrap->_len_func(wrap->_base._self);
121
122 // Iterate through the items, invoking the equality function for each, until
123 // we have found the matching one.
124 for (Py_ssize_t index = 0; index < length; ++index) {
125 PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
126 if (item != nullptr) {
127 int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
128 if (cmp > 0) {
129 return 1;
130 }
131 if (cmp < 0) {
132 return -1;
133 }
134 } else {
135 return -1;
136 }
137 }
138 return 0;
139}
140
141/**
142 * Implementation of property.index(x) which returns the index of the first
143 * occurrence of x in the sequence, or raises a ValueError if it isn't found.
144 */
145static PyObject *Dtool_SequenceWrapper_index(PyObject *self, PyObject *value) {
146 Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
147 nassertr(wrap, nullptr);
148 nassertr(wrap->_len_func, nullptr);
149 nassertr(wrap->_getitem_func, nullptr);
150
151 Py_ssize_t length = wrap->_len_func(wrap->_base._self);
152
153 // Iterate through the items, invoking the equality function for each, until
154 // we have found the right one.
155 for (Py_ssize_t index = 0; index < length; ++index) {
156 PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
157 if (item != nullptr) {
158 int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
159 if (cmp > 0) {
160 return Dtool_WrapValue(index);
161 }
162 if (cmp < 0) {
163 return nullptr;
164 }
165 } else {
166 return nullptr;
167 }
168 }
169 // Not found, raise ValueError.
170 return PyErr_Format(PyExc_ValueError, "%s.index() did not find value", wrap->_base._name);
171}
172
173/**
174 * Implementation of property.count(x) which returns the number of occurrences
175 * of x in the sequence.
176 */
177static PyObject *Dtool_SequenceWrapper_count(PyObject *self, PyObject *value) {
178 Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)self;
179 nassertr(wrap, nullptr);
180 Py_ssize_t index = 0;
181 if (wrap->_len_func != nullptr) {
182 index = wrap->_len_func(wrap->_base._self);
183 } else {
184 return Dtool_Raise_TypeError("property does not support count()");
185 }
186 // Iterate through the items, invoking the == operator for each.
187 long count = 0;
188 nassertr(wrap->_getitem_func, nullptr);
189 while (index > 0) {
190 --index;
191 PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
192 if (item == nullptr) {
193 return nullptr;
194 }
195 int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
196 if (cmp > 0) {
197 ++count;
198 }
199 if (cmp < 0) {
200 return nullptr;
201 }
202 }
203#if PY_MAJOR_VERSION >= 3
204 return PyLong_FromLong(count);
205#else
206 return PyInt_FromLong(count);
207#endif
208}
209
210/**
211 * Implementation of `property[i] = x`
212 */
213static int Dtool_MutableSequenceWrapper_setitem(PyObject *self, Py_ssize_t index, PyObject *value) {
214 Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
215 nassertr(wrap, -1);
216 if (wrap->_setitem_func != nullptr) {
217 return wrap->_setitem_func(wrap->_base._self, index, value);
218 } else {
219 Dtool_Raise_TypeError("property does not support item assignment");
220 return -1;
221 }
222}
223
224/**
225 * Implementation of property.clear() which removes all elements in the
226 * sequence, starting with the last.
227 */
228static PyObject *Dtool_MutableSequenceWrapper_clear(PyObject *self, PyObject *) {
229 Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
230 nassertr(wrap, nullptr);
231 Py_ssize_t index = 0;
232 if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) {
233 index = wrap->_len_func(wrap->_base._self);
234 } else {
235 return Dtool_Raise_TypeError("property does not support clear()");
236 }
237
238 // Iterate through the items, invoking the delete function for each. We do
239 // this in reverse order, which may be more efficient.
240 while (index > 0) {
241 --index;
242 if (wrap->_setitem_func(wrap->_base._self, index, nullptr) != 0) {
243 return nullptr;
244 }
245 }
246 Py_INCREF(Py_None);
247 return Py_None;
248}
249
250/**
251 * Implementation of property.remove(x) which removes the first occurrence of
252 * x in the sequence, or raises a ValueError if it isn't found.
253 */
254static PyObject *Dtool_MutableSequenceWrapper_remove(PyObject *self, PyObject *value) {
255 Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
256 nassertr(wrap, nullptr);
257 Py_ssize_t length = 0;
258 if (wrap->_len_func != nullptr && wrap->_setitem_func != nullptr) {
259 length = wrap->_len_func(wrap->_base._self);
260 } else {
261 return Dtool_Raise_TypeError("property does not support remove()");
262 }
263
264 // Iterate through the items, invoking the equality function for each, until
265 // we have found the right one.
266 nassertr(wrap->_getitem_func, nullptr);
267 for (Py_ssize_t index = 0; index < length; ++index) {
268 PyObject *item = wrap->_getitem_func(wrap->_base._self, index);
269 if (item != nullptr) {
270 int cmp = PyObject_RichCompareBool(item, value, Py_EQ);
271 if (cmp > 0) {
272 if (wrap->_setitem_func(wrap->_base._self, index, nullptr) == 0) {
273 Py_INCREF(Py_None);
274 return Py_None;
275 } else {
276 return nullptr;
277 }
278 }
279 if (cmp < 0) {
280 return nullptr;
281 }
282 } else {
283 return nullptr;
284 }
285 }
286 // Not found, raise ValueError.
287 return PyErr_Format(PyExc_ValueError, "%s.remove() did not find value", wrap->_base._name);
288}
289
290/**
291 * Implementation of property.pop([i=-1]) which returns and removes the
292 * element at the indicated index in the sequence. If no index is provided,
293 * it removes from the end of the list.
294 */
295static PyObject *Dtool_MutableSequenceWrapper_pop(PyObject *self, PyObject *args) {
296 Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
297 nassertr(wrap, nullptr);
298 if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr ||
299 wrap->_len_func == nullptr) {
300 return Dtool_Raise_TypeError("property does not support pop()");
301 }
302
303 Py_ssize_t length = wrap->_len_func(wrap->_base._self);
304 Py_ssize_t index;
305 switch (PyTuple_GET_SIZE(args)) {
306 case 0:
307 index = length - 1;
308 break;
309 case 1:
310 index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 0), PyExc_IndexError);
311 if (index == -1 && _PyErr_OCCURRED()) {
312 return nullptr;
313 }
314 if (index < 0) {
315 index += length;
316 }
317 break;
318 default:
319 return Dtool_Raise_TypeError("pop([i=-1]) takes 0 or 1 arguments");
320 }
321
322 if (length <= 0) {
323 return PyErr_Format(PyExc_IndexError, "%s.pop() from empty sequence", wrap->_base._name);
324 }
325
326 // Index error will be caught by getitem_func.
327 PyObject *value = wrap->_getitem_func(wrap->_base._self, index);
328 if (value != nullptr) {
329 if (wrap->_setitem_func(wrap->_base._self, index, nullptr) != 0) {
330 return nullptr;
331 }
332 return value;
333 }
334 return nullptr;
335}
336
337/**
338 * Implementation of property.append(x) which is an alias for
339 * property.insert(len(property), x).
340 */
341static PyObject *Dtool_MutableSequenceWrapper_append(PyObject *self, PyObject *arg) {
342 Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
343 nassertr(wrap, nullptr);
344 if (wrap->_insert_func == nullptr) {
345 return Dtool_Raise_TypeError("property does not support append()");
346 }
347 return wrap->_insert_func(wrap->_base._self, (size_t)-1, arg);
348}
349
350/**
351 * Implementation of property.insert(i, x) which inserts the given item at the
352 * given position.
353 */
354static PyObject *Dtool_MutableSequenceWrapper_insert(PyObject *self, PyObject *args) {
355 Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
356 nassertr(wrap, nullptr);
357 if (wrap->_insert_func == nullptr) {
358 return Dtool_Raise_TypeError("property does not support insert()");
359 }
360 if (PyTuple_GET_SIZE(args) != 2) {
361 return Dtool_Raise_TypeError("insert() takes exactly 2 arguments");
362 }
363 Py_ssize_t index = PyNumber_AsSsize_t(PyTuple_GET_ITEM(args, 0), PyExc_IndexError);
364 if (index == -1 && _PyErr_OCCURRED()) {
365 return nullptr;
366 }
367 if (index < 0) {
368 if (wrap->_len_func != nullptr) {
369 index += wrap->_len_func(wrap->_base._self);
370 } else {
371 return PyErr_Format(PyExc_TypeError, "%s.insert() does not support negative indices", wrap->_base._name);
372 }
373 }
374 return wrap->_insert_func(wrap->_base._self, (size_t)std::max(index, (Py_ssize_t)0), PyTuple_GET_ITEM(args, 1));
375}
376
377/**
378 * Implementation of property.extend(seq) which is equivalent to:
379 * @code
380 * for x in seq:
381 * property.append(seq)
382 * @endcode
383 */
384static PyObject *Dtool_MutableSequenceWrapper_extend(PyObject *self, PyObject *arg) {
385 Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)self;
386 nassertr(wrap, nullptr);
387 if (wrap->_insert_func == nullptr) {
388 return Dtool_Raise_TypeError("property does not support extend()");
389 }
390 PyObject *iter = PyObject_GetIter(arg);
391 if (iter == nullptr) {
392 return nullptr;
393 }
394 PyObject *next = PyIter_Next(iter);
395 PyObject *retval = nullptr;
396 while (next != nullptr) {
397 retval = wrap->_insert_func(wrap->_base._self, (size_t)-1, next);
398 Py_DECREF(next);
399 if (retval == nullptr) {
400 Py_DECREF(iter);
401 return nullptr;
402 }
403 Py_DECREF(retval);
404 next = PyIter_Next(iter);
405 }
406
407 Py_DECREF(iter);
408 Py_INCREF(Py_None);
409 return Py_None;
410}
411
412/**
413 * Implementation of `x in mapping`.
414 */
415static int Dtool_MappingWrapper_contains(PyObject *self, PyObject *key) {
416 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
417 nassertr(wrap, -1);
418 nassertr(wrap->_getitem_func, -1);
419 PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
420 if (value != nullptr) {
421 Py_DECREF(value);
422 return 1;
423 } else if (_PyErr_OCCURRED() == PyExc_KeyError ||
424 _PyErr_OCCURRED() == PyExc_TypeError) {
425 PyErr_Restore(nullptr, nullptr, nullptr);
426 return 0;
427 } else {
428 return -1;
429 }
430}
431
432static PyObject *Dtool_MappingWrapper_getitem(PyObject *self, PyObject *key) {
433 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
434 nassertr(wrap, nullptr);
435 nassertr(wrap->_getitem_func, nullptr);
436 return wrap->_getitem_func(wrap->_base._self, key);
437}
438
439/**
440 * Implementation of iter(property) that returns an iterable over all the
441 * keys.
442 */
443static PyObject *Dtool_MappingWrapper_iter(PyObject *self) {
444 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
445 nassertr(wrap, nullptr);
446
447 if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
448 return PyErr_Format(PyExc_TypeError, "%s is not iterable", wrap->_base._name);
449 }
450
451 Dtool_SequenceWrapper *keys = Dtool_NewSequenceWrapper(wrap->_base._self, wrap->_base._name);
452 if (keys != nullptr) {
453 keys->_len_func = wrap->_keys._len_func;
454 keys->_getitem_func = wrap->_keys._getitem_func;
455 return PySeqIter_New((PyObject *)keys);
456 } else {
457 return nullptr;
458 }
459}
460
461/**
462 * Implementation of property.get(key[,def=None]) which returns the value with
463 * the given key in the mapping, or the given default value (which defaults to
464 * None) if the key isn't found in the mapping.
465 */
466static PyObject *Dtool_MappingWrapper_get(PyObject *self, PyObject *args) {
467 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
468 nassertr(wrap, nullptr);
469 nassertr(wrap->_getitem_func, nullptr);
470 Py_ssize_t size = PyTuple_GET_SIZE(args);
471 if (size != 1 && size != 2) {
472 return PyErr_Format(PyExc_TypeError, "%s.get() takes 1 or 2 arguments", wrap->_base._name);
473 }
474 PyObject *defvalue = Py_None;
475 if (size >= 2) {
476 defvalue = PyTuple_GET_ITEM(args, 1);
477 }
478 PyObject *key = PyTuple_GET_ITEM(args, 0);
479 PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
480 if (value != nullptr) {
481 return value;
482 } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
483 PyErr_Restore(nullptr, nullptr, nullptr);
484 Py_INCREF(defvalue);
485 return defvalue;
486 } else {
487 return nullptr;
488 }
489}
490
491/**
492 * This is returned by mapping.keys().
493 */
494static PyObject *Dtool_MappingWrapper_Keys_repr(PyObject *self) {
495 Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
496 nassertr(wrap, nullptr);
497
498 PyObject *repr = PyObject_Repr(wrap->_self);
499 PyObject *result;
500#if PY_MAJOR_VERSION >= 3
501 result = PyUnicode_FromFormat("<%s.keys() of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
502#else
503 result = PyString_FromFormat("<%s.keys() of %s>", wrap->_name, PyString_AS_STRING(repr));
504#endif
505 Py_DECREF(repr);
506 return result;
507}
508
509/**
510 * Implementation of property.keys(...) that returns a view of all the keys.
511 */
512static PyObject *Dtool_MappingWrapper_keys(PyObject *self, PyObject *) {
513 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
514 nassertr(wrap, nullptr);
515
516 if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
517 return Dtool_Raise_TypeError("property does not support keys()");
518 }
519
520 Dtool_MappingWrapper *keys = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
521 if (keys == nullptr) {
522 return PyErr_NoMemory();
523 }
524
525 static PySequenceMethods seq_methods = {
526 Dtool_SequenceWrapper_length,
527 nullptr, // sq_concat
528 nullptr, // sq_repeat
529 Dtool_SequenceWrapper_getitem,
530 nullptr, // sq_slice
531 nullptr, // sq_ass_item
532 nullptr, // sq_ass_slice
533 Dtool_SequenceWrapper_contains,
534 nullptr, // sq_inplace_concat
535 nullptr, // sq_inplace_repeat
536 };
537
538 static PyTypeObject wrapper_type = {
539 PyVarObject_HEAD_INIT(nullptr, 0)
540 "sequence wrapper",
541 sizeof(Dtool_SequenceWrapper),
542 0, // tp_itemsize
543 Dtool_WrapperBase_dealloc,
544 0, // tp_vectorcall_offset
545 nullptr, // tp_getattr
546 nullptr, // tp_setattr
547 nullptr, // tp_compare
548 Dtool_MappingWrapper_Keys_repr,
549 nullptr, // tp_as_number
550 &seq_methods,
551 nullptr, // tp_as_mapping
552 nullptr, // tp_hash
553 nullptr, // tp_call
554 nullptr, // tp_str
555 PyObject_GenericGetAttr,
556 PyObject_GenericSetAttr,
557 nullptr, // tp_as_buffer
558 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
559 nullptr, // tp_doc
560 nullptr, // tp_traverse
561 nullptr, // tp_clear
562 nullptr, // tp_richcompare
563 0, // tp_weaklistoffset
564 PySeqIter_New,
565 nullptr, // tp_iternext
566 nullptr, // tp_methods
567 nullptr, // tp_members
568 nullptr, // tp_getset
569 nullptr, // tp_base
570 nullptr, // tp_dict
571 nullptr, // tp_descr_get
572 nullptr, // tp_descr_set
573 0, // tp_dictoffset
574 nullptr, // tp_init
575 PyType_GenericAlloc,
576 nullptr, // tp_new
577 PyObject_Del,
578 nullptr, // tp_is_gc
579 nullptr, // tp_bases
580 nullptr, // tp_mro
581 nullptr, // tp_cache
582 nullptr, // tp_subclasses
583 nullptr, // tp_weaklist
584 nullptr, // tp_del
585 0, // tp_version_tag,
586#if PY_VERSION_HEX >= 0x03040000
587 nullptr, // tp_finalize
588#endif
589#if PY_VERSION_HEX >= 0x03080000
590 nullptr, // tp_vectorcall
591#endif
592 };
593
594 static bool registered = false;
595 if (!registered) {
596 registered = true;
597
598 if (PyType_Ready(&wrapper_type) < 0) {
599 return nullptr;
600 }
601
602 // If the collections.abc module is loaded, register this as a subclass.
603 _register_collection((PyTypeObject *)&wrapper_type, "MappingView");
604 }
605
606 (void)PyObject_INIT(keys, &wrapper_type);
607 Py_XINCREF(wrap->_base._self);
608 keys->_base._self = wrap->_base._self;
609 keys->_base._name = wrap->_base._name;
610 keys->_keys._len_func = wrap->_keys._len_func;
611 keys->_keys._getitem_func = wrap->_keys._getitem_func;
612 keys->_getitem_func = wrap->_getitem_func;
613 keys->_setitem_func = nullptr;
614 return (PyObject *)keys;
615}
616
617/**
618 * This is returned by mapping.values().
619 */
620static PyObject *Dtool_MappingWrapper_Values_repr(PyObject *self) {
621 Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
622 nassertr(wrap, nullptr);
623
624 PyObject *repr = PyObject_Repr(wrap->_self);
625 PyObject *result;
626#if PY_MAJOR_VERSION >= 3
627 result = PyUnicode_FromFormat("<%s.values() of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
628#else
629 result = PyString_FromFormat("<%s.values() of %s>", wrap->_name, PyString_AS_STRING(repr));
630#endif
631 Py_DECREF(repr);
632 return result;
633}
634
635static PyObject *Dtool_MappingWrapper_Values_getitem(PyObject *self, Py_ssize_t index) {
636 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
637 nassertr(wrap, nullptr);
638 nassertr(wrap->_keys._getitem_func, nullptr);
639
640 PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
641 if (key != nullptr) {
642 PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
643 Py_DECREF(key);
644 return value;
645 }
646 return nullptr;
647}
648
649/**
650 * Implementation of property.values(...) that returns a view of the values.
651 */
652static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) {
653 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
654 nassertr(wrap, nullptr);
655 nassertr(wrap->_getitem_func, nullptr);
656
657 if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
658 return Dtool_Raise_TypeError("property does not support values()");
659 }
660
661 Dtool_MappingWrapper *values = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
662 if (values == nullptr) {
663 return PyErr_NoMemory();
664 }
665
666 static PySequenceMethods seq_methods = {
667 Dtool_SequenceWrapper_length,
668 nullptr, // sq_concat
669 nullptr, // sq_repeat
670 Dtool_MappingWrapper_Values_getitem,
671 nullptr, // sq_slice
672 nullptr, // sq_ass_item
673 nullptr, // sq_ass_slice
674 Dtool_MappingWrapper_contains,
675 nullptr, // sq_inplace_concat
676 nullptr, // sq_inplace_repeat
677 };
678
679 static PyTypeObject wrapper_type = {
680 PyVarObject_HEAD_INIT(nullptr, 0)
681 "sequence wrapper",
682 sizeof(Dtool_MappingWrapper),
683 0, // tp_itemsize
684 Dtool_WrapperBase_dealloc,
685 0, // tp_vectorcall_offset
686 nullptr, // tp_getattr
687 nullptr, // tp_setattr
688 nullptr, // tp_compare
689 Dtool_MappingWrapper_Values_repr,
690 nullptr, // tp_as_number
691 &seq_methods,
692 nullptr, // tp_as_mapping
693 nullptr, // tp_hash
694 nullptr, // tp_call
695 nullptr, // tp_str
696 PyObject_GenericGetAttr,
697 PyObject_GenericSetAttr,
698 nullptr, // tp_as_buffer
699 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
700 nullptr, // tp_doc
701 nullptr, // tp_traverse
702 nullptr, // tp_clear
703 nullptr, // tp_richcompare
704 0, // tp_weaklistoffset
705 PySeqIter_New,
706 nullptr, // tp_iternext
707 nullptr, // tp_methods
708 nullptr, // tp_members
709 nullptr, // tp_getset
710 nullptr, // tp_base
711 nullptr, // tp_dict
712 nullptr, // tp_descr_get
713 nullptr, // tp_descr_set
714 0, // tp_dictoffset
715 nullptr, // tp_init
716 PyType_GenericAlloc,
717 nullptr, // tp_new
718 PyObject_Del,
719 nullptr, // tp_is_gc
720 nullptr, // tp_bases
721 nullptr, // tp_mro
722 nullptr, // tp_cache
723 nullptr, // tp_subclasses
724 nullptr, // tp_weaklist
725 nullptr, // tp_del
726 0, // tp_version_tag,
727#if PY_VERSION_HEX >= 0x03040000
728 nullptr, // tp_finalize
729#endif
730#if PY_VERSION_HEX >= 0x03080000
731 nullptr, // tp_vectorcall
732#endif
733 };
734
735 static bool registered = false;
736 if (!registered) {
737 registered = true;
738
739 if (PyType_Ready(&wrapper_type) < 0) {
740 return nullptr;
741 }
742
743 // If the collections.abc module is loaded, register this as a subclass.
744 _register_collection((PyTypeObject *)&wrapper_type, "ValuesView");
745 }
746
747 (void)PyObject_INIT(values, &wrapper_type);
748 Py_XINCREF(wrap->_base._self);
749 values->_base._self = wrap->_base._self;
750 values->_base._name = wrap->_base._name;
751 values->_keys._len_func = wrap->_keys._len_func;
752 values->_keys._getitem_func = wrap->_keys._getitem_func;
753 values->_getitem_func = wrap->_getitem_func;
754 values->_setitem_func = nullptr;
755 return (PyObject *)values;
756}
757
758/**
759 * This is returned by mapping.items().
760 */
761static PyObject *Dtool_MappingWrapper_Items_repr(PyObject *self) {
762 Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
763 nassertr(wrap, nullptr);
764
765 PyObject *repr = PyObject_Repr(wrap->_self);
766 PyObject *result;
767#if PY_MAJOR_VERSION >= 3
768 result = PyUnicode_FromFormat("<%s.items() of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
769#else
770 result = PyString_FromFormat("<%s.items() of %s>", wrap->_name, PyString_AS_STRING(repr));
771#endif
772 Py_DECREF(repr);
773 return result;
774}
775
776static PyObject *Dtool_MappingWrapper_Items_getitem(PyObject *self, Py_ssize_t index) {
777 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
778 nassertr(wrap, nullptr);
779 nassertr(wrap->_keys._getitem_func, nullptr);
780
781 PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
782 if (key != nullptr) {
783 PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
784 if (value != nullptr) {
785 // PyTuple_SET_ITEM steals the reference.
786 PyObject *item = PyTuple_New(2);
787 PyTuple_SET_ITEM(item, 0, key);
788 PyTuple_SET_ITEM(item, 1, value);
789 return item;
790 } else {
791 Py_DECREF(key);
792 }
793 }
794 return nullptr;
795}
796
797/**
798 * Implementation of property.items(...) that returns an iterable yielding a
799 * `(key, value)` tuple for every item.
800 */
801static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) {
802 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
803 nassertr(wrap, nullptr);
804 nassertr(wrap->_getitem_func, nullptr);
805
806 if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
807 return Dtool_Raise_TypeError("property does not support items()");
808 }
809
810 Dtool_MappingWrapper *items = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
811 if (items == nullptr) {
812 return PyErr_NoMemory();
813 }
814
815 static PySequenceMethods seq_methods = {
816 Dtool_SequenceWrapper_length,
817 nullptr, // sq_concat
818 nullptr, // sq_repeat
819 Dtool_MappingWrapper_Items_getitem,
820 nullptr, // sq_slice
821 nullptr, // sq_ass_item
822 nullptr, // sq_ass_slice
823 Dtool_MappingWrapper_contains,
824 nullptr, // sq_inplace_concat
825 nullptr, // sq_inplace_repeat
826 };
827
828 static PyTypeObject wrapper_type = {
829 PyVarObject_HEAD_INIT(nullptr, 0)
830 "sequence wrapper",
831 sizeof(Dtool_MappingWrapper),
832 0, // tp_itemsize
833 Dtool_WrapperBase_dealloc,
834 0, // tp_vectorcall_offset
835 nullptr, // tp_getattr
836 nullptr, // tp_setattr
837 nullptr, // tp_compare
838 Dtool_MappingWrapper_Items_repr,
839 nullptr, // tp_as_number
840 &seq_methods,
841 nullptr, // tp_as_mapping
842 nullptr, // tp_hash
843 nullptr, // tp_call
844 nullptr, // tp_str
845 PyObject_GenericGetAttr,
846 PyObject_GenericSetAttr,
847 nullptr, // tp_as_buffer
848 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
849 nullptr, // tp_doc
850 nullptr, // tp_traverse
851 nullptr, // tp_clear
852 nullptr, // tp_richcompare
853 0, // tp_weaklistoffset
854 PySeqIter_New,
855 nullptr, // tp_iternext
856 nullptr, // tp_methods
857 nullptr, // tp_members
858 nullptr, // tp_getset
859 nullptr, // tp_base
860 nullptr, // tp_dict
861 nullptr, // tp_descr_get
862 nullptr, // tp_descr_set
863 0, // tp_dictoffset
864 nullptr, // tp_init
865 PyType_GenericAlloc,
866 nullptr, // tp_new
867 PyObject_Del,
868 nullptr, // tp_is_gc
869 nullptr, // tp_bases
870 nullptr, // tp_mro
871 nullptr, // tp_cache
872 nullptr, // tp_subclasses
873 nullptr, // tp_weaklist
874 nullptr, // tp_del
875 0, // tp_version_tag,
876#if PY_VERSION_HEX >= 0x03040000
877 nullptr, // tp_finalize
878#endif
879#if PY_VERSION_HEX >= 0x03080000
880 nullptr, // tp_vectorcall
881#endif
882 };
883
884 static bool registered = false;
885 if (!registered) {
886 registered = true;
887
888 if (PyType_Ready(&wrapper_type) < 0) {
889 return nullptr;
890 }
891
892 // If the collections.abc module is loaded, register this as a subclass.
893 _register_collection((PyTypeObject *)&wrapper_type, "MappingView");
894 }
895
896 (void)PyObject_INIT(items, &wrapper_type);
897 Py_XINCREF(wrap->_base._self);
898 items->_base._self = wrap->_base._self;
899 items->_base._name = wrap->_base._name;
900 items->_keys._len_func = wrap->_keys._len_func;
901 items->_keys._getitem_func = wrap->_keys._getitem_func;
902 items->_getitem_func = wrap->_getitem_func;
903 items->_setitem_func = nullptr;
904 return (PyObject *)items;
905}
906
907/**
908 * Implementation of `property[key] = value`
909 */
910static int Dtool_MutableMappingWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
911 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
912 nassertr(wrap->_setitem_func != nullptr, -1);
913 return wrap->_setitem_func(wrap->_base._self, key, value);
914}
915
916/**
917 * Implementation of property.pop(key[,def=None]) which is the same as get()
918 * except that it also removes the element from the mapping.
919 */
920static PyObject *Dtool_MutableMappingWrapper_pop(PyObject *self, PyObject *args) {
921 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
922 nassertr(wrap, nullptr);
923 if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
924 return Dtool_Raise_TypeError("property does not support pop()");
925 }
926
927 Py_ssize_t size = PyTuple_GET_SIZE(args);
928 if (size != 1 && size != 2) {
929 return PyErr_Format(PyExc_TypeError, "%s.pop() takes 1 or 2 arguments", wrap->_base._name);
930 }
931 PyObject *defvalue = Py_None;
932 if (size >= 2) {
933 defvalue = PyTuple_GET_ITEM(args, 1);
934 }
935
936 PyObject *key = PyTuple_GET_ITEM(args, 0);
937 PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
938 if (value != nullptr) {
939 // OK, now set unset this value.
940 if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) {
941 return value;
942 } else {
943 Py_DECREF(value);
944 return nullptr;
945 }
946 } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
947 PyErr_Restore(nullptr, nullptr, nullptr);
948 Py_INCREF(defvalue);
949 return defvalue;
950 } else {
951 return nullptr;
952 }
953}
954
955/**
956 * Implementation of property.popitem() which returns and removes an arbitrary
957 * (key, value) pair from the mapping. Useful for destructive iteration.
958 */
959static PyObject *Dtool_MutableMappingWrapper_popitem(PyObject *self, PyObject *) {
960 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
961 nassertr(wrap, nullptr);
962 if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr ||
963 wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
964 return Dtool_Raise_TypeError("property does not support popitem()");
965 }
966
967 Py_ssize_t length = wrap->_keys._len_func(wrap->_base._self);
968 if (length < 1) {
969 return PyErr_Format(PyExc_KeyError, "%s is empty", wrap->_base._name);
970 }
971
972 PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, length - 1);
973 if (key != nullptr) {
974 PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
975 if (value != nullptr) {
976 // OK, now set unset this value.
977 if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) {
978 PyObject *item = PyTuple_New(2);
979 PyTuple_SET_ITEM(item, 0, key);
980 PyTuple_SET_ITEM(item, 1, value);
981 return item;
982 }
983 Py_DECREF(value);
984 }
985 }
986 return nullptr;
987}
988
989/*
990 * Implementation of property.clear() which removes all elements in the
991 * mapping.
992 */
993static PyObject *Dtool_MutableMappingWrapper_clear(PyObject *self, PyObject *) {
994 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
995 nassertr(wrap, nullptr);
996 Py_ssize_t index = 0;
997 if (wrap->_keys._len_func != nullptr && wrap->_keys._getitem_func != nullptr &&
998 wrap->_setitem_func != nullptr) {
999 index = wrap->_keys._len_func(wrap->_base._self);
1000 } else {
1001 return Dtool_Raise_TypeError("property does not support clear()");
1002 }
1003
1004 // Iterate through the items, invoking the delete function for each. We do
1005 // this in reverse order, which may be more efficient.
1006 while (index > 0) {
1007 --index;
1008 PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
1009 if (key != nullptr) {
1010 int result = wrap->_setitem_func(wrap->_base._self, key, nullptr);
1011 Py_DECREF(key);
1012 if (result != 0) {
1013 return nullptr;
1014 }
1015 }
1016 }
1017 Py_INCREF(Py_None);
1018 return Py_None;
1019}
1020
1021/**
1022 * Implementation of property.setdefault(key[,def=None]) which is the same as
1023 * get() except that it also writes the default value back to the mapping if
1024 * the key was not found is missing.
1025 */
1026static PyObject *Dtool_MutableMappingWrapper_setdefault(PyObject *self, PyObject *args) {
1027 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
1028 nassertr(wrap, nullptr);
1029
1030 if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
1031 return Dtool_Raise_TypeError("property does not support setdefault()");
1032 }
1033
1034 Py_ssize_t size = PyTuple_GET_SIZE(args);
1035 if (size != 1 && size != 2) {
1036 return PyErr_Format(PyExc_TypeError, "%s.setdefault() takes 1 or 2 arguments", wrap->_base._name);
1037 }
1038 PyObject *defvalue = Py_None;
1039 if (size >= 2) {
1040 defvalue = PyTuple_GET_ITEM(args, 1);
1041 }
1042 PyObject *key = PyTuple_GET_ITEM(args, 0);
1043 PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
1044 if (value != nullptr) {
1045 return value;
1046 } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
1047 PyErr_Restore(nullptr, nullptr, nullptr);
1048 if (wrap->_setitem_func(wrap->_base._self, key, defvalue) == 0) {
1049 Py_INCREF(defvalue);
1050 return defvalue;
1051 }
1052 }
1053 return nullptr;
1054}
1055
1056/**
1057 * Implementation of property.update(...) which sets multiple values in one
1058 * go. It accepts either a single dictionary or keyword arguments, not both.
1059 */
1060static PyObject *Dtool_MutableMappingWrapper_update(PyObject *self, PyObject *args, PyObject *kwargs) {
1061 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
1062 nassertr(wrap, nullptr);
1063
1064 if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
1065 return Dtool_Raise_TypeError("property does not support update()");
1066 }
1067
1068 // We accept either a dict argument or keyword arguments, but not both.
1069 PyObject *dict;
1070 switch (PyTuple_GET_SIZE(args)) {
1071 case 0:
1072 if (kwargs == nullptr) {
1073 // This is legal.
1074 Py_INCREF(Py_None);
1075 return Py_None;
1076 }
1077 dict = kwargs;
1078 break;
1079 case 1:
1080 if (PyDict_Check(PyTuple_GET_ITEM(args, 0)) && (kwargs == nullptr || Py_SIZE(kwargs) == 0)) {
1081 dict = PyTuple_GET_ITEM(args, 0);
1082 break;
1083 }
1084 // Fall through
1085 default:
1086 return PyErr_Format(PyExc_TypeError, "%s.update() takes either a dict argument or keyword arguments", wrap->_base._name);
1087 }
1088
1089 PyObject *key, *value;
1090 Py_ssize_t pos = 0;
1091 while (PyDict_Next(dict, &pos, &key, &value)) {
1092 if (wrap->_setitem_func(wrap->_base._self, key, value) != 0) {
1093 return nullptr;
1094 }
1095 }
1096 Py_INCREF(Py_None);
1097 return Py_None;
1098}
1099
1100/**
1101 * This variant defines only a generator interface.
1102 */
1103static PyObject *Dtool_GeneratorWrapper_iternext(PyObject *self) {
1104 Dtool_GeneratorWrapper *wrap = (Dtool_GeneratorWrapper *)self;
1105 nassertr(wrap, nullptr);
1106 nassertr(wrap->_iternext_func, nullptr);
1107 return wrap->_iternext_func(wrap->_base._self);
1108}
1109
1110/**
1111 * This is a variant of the Python getset mechanism that permits static
1112 * properties.
1113 */
1114static void
1115Dtool_StaticProperty_dealloc(PyDescrObject *descr) {
1116#if PY_VERSION_HEX >= 0x03080000
1117 PyObject_GC_UnTrack(descr);
1118#else
1119 _PyObject_GC_UNTRACK(descr);
1120#endif
1121 Py_XDECREF(descr->d_type);
1122 Py_XDECREF(descr->d_name);
1123//#if PY_MAJOR_VERSION >= 3
1124// Py_XDECREF(descr->d_qualname);
1125//#endif
1126 PyObject_GC_Del(descr);
1127}
1128
1129static PyObject *
1130Dtool_StaticProperty_repr(PyDescrObject *descr, const char *format) {
1131#if PY_MAJOR_VERSION >= 3
1132 return PyUnicode_FromFormat("<attribute '%s' of '%s'>",
1133 PyUnicode_AsUTF8(descr->d_name),
1134 descr->d_type->tp_name);
1135#else
1136 return PyString_FromFormat("<attribute '%s' of '%s'>",
1137 PyString_AS_STRING(descr->d_name),
1138 descr->d_type->tp_name);
1139#endif
1140}
1141
1142static int
1143Dtool_StaticProperty_traverse(PyObject *self, visitproc visit, void *arg) {
1144 PyDescrObject *descr = (PyDescrObject *)self;
1145 Py_VISIT(descr->d_type);
1146 return 0;
1147}
1148
1149static PyObject *
1150Dtool_StaticProperty_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type) {
1151 if (descr->d_getset->get != nullptr) {
1152 return descr->d_getset->get(obj, descr->d_getset->closure);
1153 } else {
1154 return PyErr_Format(PyExc_AttributeError,
1155 "attribute '%s' of type '%.100s' is not readable",
1156#if PY_MAJOR_VERSION >= 3
1157 PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name),
1158#else
1159 PyString_AS_STRING(((PyDescrObject *)descr)->d_name),
1160#endif
1161 ((PyDescrObject *)descr)->d_type->tp_name);
1162 }
1163}
1164
1165static int
1166Dtool_StaticProperty_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) {
1167 if (descr->d_getset->set != nullptr) {
1168 return descr->d_getset->set(obj, value, descr->d_getset->closure);
1169 } else {
1170 PyErr_Format(PyExc_AttributeError,
1171 "attribute '%s' of type '%.100s' is not writable",
1172#if PY_MAJOR_VERSION >= 3
1173 PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name),
1174#else
1175 PyString_AS_STRING(((PyDescrObject *)descr)->d_name),
1176#endif
1177 ((PyDescrObject *)descr)->d_type->tp_name);
1178 return -1;
1179 }
1180}
1181
1182/**
1183 * This wraps around a property that exposes a sequence interface.
1184 */
1185Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name) {
1186 Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)PyObject_MALLOC(sizeof(Dtool_SequenceWrapper));
1187 if (wrap == nullptr) {
1188 return (Dtool_SequenceWrapper *)PyErr_NoMemory();
1189 }
1190
1191 static PySequenceMethods seq_methods = {
1192 Dtool_SequenceWrapper_length,
1193 nullptr, // sq_concat
1194 nullptr, // sq_repeat
1195 Dtool_SequenceWrapper_getitem,
1196 nullptr, // sq_slice
1197 nullptr, // sq_ass_item
1198 nullptr, // sq_ass_slice
1199 Dtool_SequenceWrapper_contains,
1200 nullptr, // sq_inplace_concat
1201 nullptr, // sq_inplace_repeat
1202 };
1203
1204 static PyMethodDef methods[] = {
1205 {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr},
1206 {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr},
1207 {nullptr, nullptr, 0, nullptr}
1208 };
1209
1210 static PyTypeObject wrapper_type = {
1211 PyVarObject_HEAD_INIT(nullptr, 0)
1212 "sequence wrapper",
1213 sizeof(Dtool_SequenceWrapper),
1214 0, // tp_itemsize
1215 Dtool_WrapperBase_dealloc,
1216 0, // tp_vectorcall_offset
1217 nullptr, // tp_getattr
1218 nullptr, // tp_setattr
1219 nullptr, // tp_compare
1220 Dtool_SequenceWrapper_repr,
1221 nullptr, // tp_as_number
1222 &seq_methods,
1223 nullptr, // tp_as_mapping
1224 nullptr, // tp_hash
1225 nullptr, // tp_call
1226 nullptr, // tp_str
1227 PyObject_GenericGetAttr,
1228 PyObject_GenericSetAttr,
1229 nullptr, // tp_as_buffer
1230 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1231 nullptr, // tp_doc
1232 nullptr, // tp_traverse
1233 nullptr, // tp_clear
1234 nullptr, // tp_richcompare
1235 0, // tp_weaklistoffset
1236 PySeqIter_New,
1237 nullptr, // tp_iternext
1238 methods,
1239 nullptr, // tp_members
1240 nullptr, // tp_getset
1241 nullptr, // tp_base
1242 nullptr, // tp_dict
1243 nullptr, // tp_descr_get
1244 nullptr, // tp_descr_set
1245 0, // tp_dictoffset
1246 nullptr, // tp_init
1247 PyType_GenericAlloc,
1248 nullptr, // tp_new
1249 PyObject_Del,
1250 nullptr, // tp_is_gc
1251 nullptr, // tp_bases
1252 nullptr, // tp_mro
1253 nullptr, // tp_cache
1254 nullptr, // tp_subclasses
1255 nullptr, // tp_weaklist
1256 nullptr, // tp_del
1257 0, // tp_version_tag,
1258#if PY_VERSION_HEX >= 0x03040000
1259 nullptr, // tp_finalize
1260#endif
1261#if PY_VERSION_HEX >= 0x03080000
1262 nullptr, // tp_vectorcall
1263#endif
1264 };
1265
1266 static bool registered = false;
1267 if (!registered) {
1268 registered = true;
1269
1270 if (PyType_Ready(&wrapper_type) < 0) {
1271 return nullptr;
1272 }
1273
1274 // If the collections.abc module is loaded, register this as a subclass.
1275 _register_collection((PyTypeObject *)&wrapper_type, "Sequence");
1276 }
1277
1278 (void)PyObject_INIT(wrap, &wrapper_type);
1279 Py_XINCREF(self);
1280 wrap->_base._self = self;
1281 wrap->_base._name = name;
1282 wrap->_len_func = nullptr;
1283 wrap->_getitem_func = nullptr;
1284 return wrap;
1285}
1286
1287/**
1288 * This wraps around a property that exposes a mutable sequence interface.
1289 */
1290Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name) {
1291 Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)PyObject_MALLOC(sizeof(Dtool_MutableSequenceWrapper));
1292 if (wrap == nullptr) {
1293 return (Dtool_MutableSequenceWrapper *)PyErr_NoMemory();
1294 }
1295
1296 static PySequenceMethods seq_methods = {
1297 Dtool_SequenceWrapper_length,
1298 nullptr, // sq_concat
1299 nullptr, // sq_repeat
1300 Dtool_SequenceWrapper_getitem,
1301 nullptr, // sq_slice
1302 Dtool_MutableSequenceWrapper_setitem,
1303 nullptr, // sq_ass_slice
1304 Dtool_SequenceWrapper_contains,
1305 Dtool_MutableSequenceWrapper_extend,
1306 nullptr, // sq_inplace_repeat
1307 };
1308
1309 static PyMethodDef methods[] = {
1310 {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr},
1311 {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr},
1312 {"clear", &Dtool_MutableSequenceWrapper_clear, METH_NOARGS, nullptr},
1313 {"pop", &Dtool_MutableSequenceWrapper_pop, METH_VARARGS, nullptr},
1314 {"remove", &Dtool_MutableSequenceWrapper_remove, METH_O, nullptr},
1315 {"append", &Dtool_MutableSequenceWrapper_append, METH_O, nullptr},
1316 {"insert", &Dtool_MutableSequenceWrapper_insert, METH_VARARGS, nullptr},
1317 {"extend", &Dtool_MutableSequenceWrapper_extend, METH_O, nullptr},
1318 {nullptr, nullptr, 0, nullptr}
1319 };
1320
1321 static PyTypeObject wrapper_type = {
1322 PyVarObject_HEAD_INIT(nullptr, 0)
1323 "sequence wrapper",
1324 sizeof(Dtool_MutableSequenceWrapper),
1325 0, // tp_itemsize
1326 Dtool_WrapperBase_dealloc,
1327 0, // tp_vectorcall_offset
1328 nullptr, // tp_getattr
1329 nullptr, // tp_setattr
1330 nullptr, // tp_compare
1331 Dtool_SequenceWrapper_repr,
1332 nullptr, // tp_as_number
1333 &seq_methods,
1334 nullptr, // tp_as_mapping
1335 nullptr, // tp_hash
1336 nullptr, // tp_call
1337 nullptr, // tp_str
1338 PyObject_GenericGetAttr,
1339 PyObject_GenericSetAttr,
1340 nullptr, // tp_as_buffer
1341 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1342 nullptr, // tp_doc
1343 nullptr, // tp_traverse
1344 nullptr, // tp_clear
1345 nullptr, // tp_richcompare
1346 0, // tp_weaklistoffset
1347 PySeqIter_New,
1348 nullptr, // tp_iternext
1349 methods,
1350 nullptr, // tp_members
1351 nullptr, // tp_getset
1352 nullptr, // tp_base
1353 nullptr, // tp_dict
1354 nullptr, // tp_descr_get
1355 nullptr, // tp_descr_set
1356 0, // tp_dictoffset
1357 nullptr, // tp_init
1358 PyType_GenericAlloc,
1359 nullptr, // tp_new
1360 PyObject_Del,
1361 nullptr, // tp_is_gc
1362 nullptr, // tp_bases
1363 nullptr, // tp_mro
1364 nullptr, // tp_cache
1365 nullptr, // tp_subclasses
1366 nullptr, // tp_weaklist
1367 nullptr, // tp_del
1368 0, // tp_version_tag,
1369#if PY_VERSION_HEX >= 0x03040000
1370 nullptr, // tp_finalize
1371#endif
1372#if PY_VERSION_HEX >= 0x03080000
1373 nullptr, // tp_vectorcall
1374#endif
1375 };
1376
1377 static bool registered = false;
1378 if (!registered) {
1379 registered = true;
1380
1381 if (PyType_Ready(&wrapper_type) < 0) {
1382 return nullptr;
1383 }
1384
1385 // If the collections.abc module is loaded, register this as a subclass.
1386 _register_collection((PyTypeObject *)&wrapper_type, "MutableSequence");
1387 }
1388
1389 (void)PyObject_INIT(wrap, &wrapper_type);
1390 Py_XINCREF(self);
1391 wrap->_base._self = self;
1392 wrap->_base._name = name;
1393 wrap->_len_func = nullptr;
1394 wrap->_getitem_func = nullptr;
1395 wrap->_setitem_func = nullptr;
1396 wrap->_insert_func = nullptr;
1397 return wrap;
1398}
1399
1400/**
1401 * This wraps around a mapping interface, with getitem function.
1402 */
1403Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name) {
1404 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
1405 if (wrap == nullptr) {
1406 return (Dtool_MappingWrapper *)PyErr_NoMemory();
1407 }
1408
1409 static PySequenceMethods seq_methods = {
1410 Dtool_SequenceWrapper_length,
1411 nullptr, // sq_concat
1412 nullptr, // sq_repeat
1413 nullptr, // sq_item
1414 nullptr, // sq_slice
1415 nullptr, // sq_ass_item
1416 nullptr, // sq_ass_slice
1417 Dtool_MappingWrapper_contains,
1418 nullptr, // sq_inplace_concat
1419 nullptr, // sq_inplace_repeat
1420 };
1421
1422 static PyMappingMethods map_methods = {
1423 Dtool_SequenceWrapper_length,
1424 Dtool_MappingWrapper_getitem,
1425 nullptr, // mp_ass_subscript
1426 };
1427
1428 static PyMethodDef methods[] = {
1429 {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr},
1430 {"keys", &Dtool_MappingWrapper_keys, METH_NOARGS, nullptr},
1431 {"values", &Dtool_MappingWrapper_values, METH_NOARGS, nullptr},
1432 {"items", &Dtool_MappingWrapper_items, METH_NOARGS, nullptr},
1433 {nullptr, nullptr, 0, nullptr}
1434 };
1435
1436 static PyTypeObject wrapper_type = {
1437 PyVarObject_HEAD_INIT(nullptr, 0)
1438 "mapping wrapper",
1439 sizeof(Dtool_MappingWrapper),
1440 0, // tp_itemsize
1441 Dtool_WrapperBase_dealloc,
1442 0, // tp_vectorcall_offset
1443 nullptr, // tp_getattr
1444 nullptr, // tp_setattr
1445 nullptr, // tp_compare
1446 Dtool_WrapperBase_repr,
1447 nullptr, // tp_as_number
1448 &seq_methods,
1449 &map_methods,
1450 nullptr, // tp_hash
1451 nullptr, // tp_call
1452 nullptr, // tp_str
1453 PyObject_GenericGetAttr,
1454 PyObject_GenericSetAttr,
1455 nullptr, // tp_as_buffer
1456 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1457 nullptr, // tp_doc
1458 nullptr, // tp_traverse
1459 nullptr, // tp_clear
1460 nullptr, // tp_richcompare
1461 0, // tp_weaklistoffset
1462 Dtool_MappingWrapper_iter,
1463 nullptr, // tp_iternext
1464 methods,
1465 nullptr, // tp_members
1466 nullptr, // tp_getset
1467 nullptr, // tp_base
1468 nullptr, // tp_dict
1469 nullptr, // tp_descr_get
1470 nullptr, // tp_descr_set
1471 0, // tp_dictoffset
1472 nullptr, // tp_init
1473 PyType_GenericAlloc,
1474 nullptr, // tp_new
1475 PyObject_Del,
1476 nullptr, // tp_is_gc
1477 nullptr, // tp_bases
1478 nullptr, // tp_mro
1479 nullptr, // tp_cache
1480 nullptr, // tp_subclasses
1481 nullptr, // tp_weaklist
1482 nullptr, // tp_del
1483 0, // tp_version_tag,
1484#if PY_VERSION_HEX >= 0x03040000
1485 nullptr, // tp_finalize
1486#endif
1487#if PY_VERSION_HEX >= 0x03080000
1488 nullptr, // tp_vectorcall
1489#endif
1490 };
1491
1492 static bool registered = false;
1493 if (!registered) {
1494 registered = true;
1495
1496 if (PyType_Ready(&wrapper_type) < 0) {
1497 return nullptr;
1498 }
1499
1500 // If the collections.abc module is loaded, register this as a subclass.
1501 _register_collection((PyTypeObject *)&wrapper_type, "Mapping");
1502 }
1503
1504 (void)PyObject_INIT(wrap, &wrapper_type);
1505 Py_XINCREF(self);
1506 wrap->_base._self = self;
1507 wrap->_base._name = name;
1508 wrap->_keys._len_func = nullptr;
1509 wrap->_keys._getitem_func = nullptr;
1510 wrap->_getitem_func = nullptr;
1511 wrap->_setitem_func = nullptr;
1512 return wrap;
1513}
1514
1515/**
1516 * This wraps around a mapping interface, with getitem/setitem functions.
1517 */
1518Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name) {
1519 Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
1520 if (wrap == nullptr) {
1521 return (Dtool_MappingWrapper *)PyErr_NoMemory();
1522 }
1523
1524 static PySequenceMethods seq_methods = {
1525 Dtool_SequenceWrapper_length,
1526 nullptr, // sq_concat
1527 nullptr, // sq_repeat
1528 nullptr, // sq_item
1529 nullptr, // sq_slice
1530 nullptr, // sq_ass_item
1531 nullptr, // sq_ass_slice
1532 Dtool_MappingWrapper_contains,
1533 nullptr, // sq_inplace_concat
1534 nullptr, // sq_inplace_repeat
1535 };
1536
1537 static PyMappingMethods map_methods = {
1538 Dtool_SequenceWrapper_length,
1539 Dtool_MappingWrapper_getitem,
1540 Dtool_MutableMappingWrapper_setitem,
1541 };
1542
1543 static PyMethodDef methods[] = {
1544 {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr},
1545 {"pop", &Dtool_MutableMappingWrapper_pop, METH_VARARGS, nullptr},
1546 {"popitem", &Dtool_MutableMappingWrapper_popitem, METH_NOARGS, nullptr},
1547 {"clear", &Dtool_MutableMappingWrapper_clear, METH_VARARGS, nullptr},
1548 {"setdefault", &Dtool_MutableMappingWrapper_setdefault, METH_VARARGS, nullptr},
1549 {"update", (PyCFunction) &Dtool_MutableMappingWrapper_update, METH_VARARGS | METH_KEYWORDS, nullptr},
1550 {"keys", &Dtool_MappingWrapper_keys, METH_NOARGS, nullptr},
1551 {"values", &Dtool_MappingWrapper_values, METH_NOARGS, nullptr},
1552 {"items", &Dtool_MappingWrapper_items, METH_NOARGS, nullptr},
1553 {nullptr, nullptr, 0, nullptr}
1554 };
1555
1556 static PyTypeObject wrapper_type = {
1557 PyVarObject_HEAD_INIT(nullptr, 0)
1558 "mapping wrapper",
1559 sizeof(Dtool_MappingWrapper),
1560 0, // tp_itemsize
1561 Dtool_WrapperBase_dealloc,
1562 0, // tp_vectorcall_offset
1563 nullptr, // tp_getattr
1564 nullptr, // tp_setattr
1565 nullptr, // tp_compare
1566 Dtool_WrapperBase_repr,
1567 nullptr, // tp_as_number
1568 &seq_methods,
1569 &map_methods,
1570 nullptr, // tp_hash
1571 nullptr, // tp_call
1572 nullptr, // tp_str
1573 PyObject_GenericGetAttr,
1574 PyObject_GenericSetAttr,
1575 nullptr, // tp_as_buffer
1576 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1577 nullptr, // tp_doc
1578 nullptr, // tp_traverse
1579 nullptr, // tp_clear
1580 nullptr, // tp_richcompare
1581 0, // tp_weaklistoffset
1582 Dtool_MappingWrapper_iter,
1583 nullptr, // tp_iternext
1584 methods,
1585 nullptr, // tp_members
1586 nullptr, // tp_getset
1587 nullptr, // tp_base
1588 nullptr, // tp_dict
1589 nullptr, // tp_descr_get
1590 nullptr, // tp_descr_set
1591 0, // tp_dictoffset
1592 nullptr, // tp_init
1593 PyType_GenericAlloc,
1594 nullptr, // tp_new
1595 PyObject_Del,
1596 nullptr, // tp_is_gc
1597 nullptr, // tp_bases
1598 nullptr, // tp_mro
1599 nullptr, // tp_cache
1600 nullptr, // tp_subclasses
1601 nullptr, // tp_weaklist
1602 nullptr, // tp_del
1603 0, // tp_version_tag,
1604#if PY_VERSION_HEX >= 0x03040000
1605 nullptr, // tp_finalize
1606#endif
1607#if PY_VERSION_HEX >= 0x03080000
1608 nullptr, // tp_vectorcall
1609#endif
1610 };
1611
1612 static bool registered = false;
1613 if (!registered) {
1614 registered = true;
1615
1616 if (PyType_Ready(&wrapper_type) < 0) {
1617 return nullptr;
1618 }
1619
1620 // If the collections.abc module is loaded, register this as a subclass.
1621 _register_collection((PyTypeObject *)&wrapper_type, "MutableMapping");
1622 }
1623
1624 (void)PyObject_INIT(wrap, &wrapper_type);
1625 Py_XINCREF(self);
1626 wrap->_base._self = self;
1627 wrap->_base._name = name;
1628 wrap->_keys._len_func = nullptr;
1629 wrap->_keys._getitem_func = nullptr;
1630 wrap->_getitem_func = nullptr;
1631 wrap->_setitem_func = nullptr;
1632 return wrap;
1633}
1634
1635/**
1636 * Creates a generator that invokes a given function with the given self arg.
1637 */
1638PyObject *
1639Dtool_NewGenerator(PyObject *self, iternextfunc gen_next) {
1640 static PyTypeObject wrapper_type = {
1641 PyVarObject_HEAD_INIT(nullptr, 0)
1642 "generator wrapper",
1643 sizeof(Dtool_GeneratorWrapper),
1644 0, // tp_itemsize
1645 Dtool_WrapperBase_dealloc,
1646 0, // tp_vectorcall_offset
1647 nullptr, // tp_getattr
1648 nullptr, // tp_setattr
1649 nullptr, // tp_compare
1650 nullptr, // tp_repr
1651 nullptr, // tp_as_number
1652 nullptr, // tp_as_sequence
1653 nullptr, // tp_as_mapping
1654 nullptr, // tp_hash
1655 nullptr, // tp_call
1656 nullptr, // tp_str
1657 PyObject_GenericGetAttr,
1658 PyObject_GenericSetAttr,
1659 nullptr, // tp_as_buffer
1660 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1661 nullptr, // tp_doc
1662 nullptr, // tp_traverse
1663 nullptr, // tp_clear
1664 nullptr, // tp_richcompare
1665 0, // tp_weaklistoffset
1666 PyObject_SelfIter,
1667 Dtool_GeneratorWrapper_iternext,
1668 nullptr, // tp_methods
1669 nullptr, // tp_members
1670 nullptr, // tp_getset
1671 nullptr, // tp_base
1672 nullptr, // tp_dict
1673 nullptr, // tp_descr_get
1674 nullptr, // tp_descr_set
1675 0, // tp_dictoffset
1676 nullptr, // tp_init
1677 PyType_GenericAlloc,
1678 nullptr, // tp_new
1679 PyObject_Del,
1680 nullptr, // tp_is_gc
1681 nullptr, // tp_bases
1682 nullptr, // tp_mro
1683 nullptr, // tp_cache
1684 nullptr, // tp_subclasses
1685 nullptr, // tp_weaklist
1686 nullptr, // tp_del
1687 0, // tp_version_tag,
1688#if PY_VERSION_HEX >= 0x03040000
1689 nullptr, // tp_finalize
1690#endif
1691#if PY_VERSION_HEX >= 0x03080000
1692 nullptr, // tp_vectorcall
1693#endif
1694 };
1695
1696 if (PyType_Ready(&wrapper_type) < 0) {
1697 return nullptr;
1698 }
1699
1700 Dtool_GeneratorWrapper *gen;
1701 gen = (Dtool_GeneratorWrapper *)PyType_GenericAlloc(&wrapper_type, 0);
1702 if (gen != nullptr) {
1703 Py_INCREF(self);
1704 gen->_base._self = self;
1705 gen->_iternext_func = gen_next;
1706 }
1707 return (PyObject *)gen;
1708}
1709
1710/**
1711 * This is a variant of the Python getset mechanism that permits static
1712 * properties.
1713 */
1714PyObject *
1715Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
1716 static PyTypeObject wrapper_type = {
1717 PyVarObject_HEAD_INIT(&PyType_Type, 0)
1718 "getset_descriptor",
1719 sizeof(PyGetSetDescrObject),
1720 0, // tp_itemsize
1721 (destructor)Dtool_StaticProperty_dealloc,
1722 0, // tp_vectorcall_offset
1723 nullptr, // tp_getattr
1724 nullptr, // tp_setattr
1725 nullptr, // tp_reserved
1726 (reprfunc)Dtool_StaticProperty_repr,
1727 nullptr, // tp_as_number
1728 nullptr, // tp_as_sequence
1729 nullptr, // tp_as_mapping
1730 nullptr, // tp_hash
1731 nullptr, // tp_call
1732 nullptr, // tp_str
1733 PyObject_GenericGetAttr,
1734 nullptr, // tp_setattro
1735 nullptr, // tp_as_buffer
1736 Py_TPFLAGS_DEFAULT,
1737 nullptr, // tp_doc
1738 Dtool_StaticProperty_traverse,
1739 nullptr, // tp_clear
1740 nullptr, // tp_richcompare
1741 0, // tp_weaklistoffset
1742 nullptr, // tp_iter
1743 nullptr, // tp_iternext
1744 nullptr, // tp_methods
1745 nullptr, // tp_members
1746 nullptr, // tp_getset
1747 nullptr, // tp_base
1748 nullptr, // tp_dict
1749 (descrgetfunc)Dtool_StaticProperty_get,
1750 (descrsetfunc)Dtool_StaticProperty_set,
1751 0, // tp_dictoffset
1752 nullptr, // tp_init
1753 nullptr, // tp_alloc
1754 nullptr, // tp_new
1755 nullptr, // tp_free
1756 nullptr, // tp_is_gc
1757 nullptr, // tp_bases
1758 nullptr, // tp_mro
1759 nullptr, // tp_cache
1760 nullptr, // tp_subclasses
1761 nullptr, // tp_weaklist
1762 nullptr, // tp_del
1763 0, // tp_version_tag,
1764#if PY_VERSION_HEX >= 0x03040000
1765 nullptr, // tp_finalize
1766#endif
1767#if PY_VERSION_HEX >= 0x03080000
1768 nullptr, // tp_vectorcall
1769#endif
1770 };
1771
1772 if (PyType_Ready(&wrapper_type) < 0) {
1773 return nullptr;
1774 }
1775
1776 PyGetSetDescrObject *descr;
1777 descr = (PyGetSetDescrObject *)PyType_GenericAlloc(&wrapper_type, 0);
1778 if (descr != nullptr) {
1779 Py_XINCREF(type);
1780 descr->d_getset = (PyGetSetDef *)getset;
1781#if PY_MAJOR_VERSION >= 3
1782 descr->d_common.d_type = type;
1783 descr->d_common.d_name = PyUnicode_InternFromString(getset->name);
1784#if PY_VERSION_HEX >= 0x03030000
1785 descr->d_common.d_qualname = nullptr;
1786#endif
1787#else
1788 descr->d_type = type;
1789 descr->d_name = PyString_InternFromString(getset->name);
1790#endif
1791 }
1792 return (PyObject *)descr;
1793}
1794
1795#endif // HAVE_PYTHON
PyObject * Dtool_WrapValue(int value)
The following functions wrap an arbitrary C++ value into a PyObject.
Definition py_panda.I:183