Panda3D
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 
19 static 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  */
46 static 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 
53 static 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 
68 static 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 
93 static 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 
104 static 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  */
114 static 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  */
145 static 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  */
177 static 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  */
213 static 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  */
228 static 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  */
254 static 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  */
295 static 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  */
341 static 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  */
354 static 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, (ssize_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  */
384 static 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  */
415 static 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 
432 static 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  */
443 static 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  */
466 static 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  */
494 static 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  */
512 static 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  nullptr, // tp_print
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  };
586 
587  static bool registered = false;
588  if (!registered) {
589  registered = true;
590 
591  if (PyType_Ready(&wrapper_type) < 0) {
592  return nullptr;
593  }
594 
595  // If the collections.abc module is loaded, register this as a subclass.
596  _register_collection((PyTypeObject *)&wrapper_type, "MappingView");
597  }
598 
599  (void)PyObject_INIT(keys, &wrapper_type);
600  Py_XINCREF(wrap->_base._self);
601  keys->_base._self = wrap->_base._self;
602  keys->_base._name = wrap->_base._name;
603  keys->_keys._len_func = wrap->_keys._len_func;
604  keys->_keys._getitem_func = wrap->_keys._getitem_func;
605  keys->_getitem_func = wrap->_getitem_func;
606  keys->_setitem_func = nullptr;
607  return (PyObject *)keys;
608 }
609 
610 /**
611  * This is returned by mapping.values().
612  */
613 static PyObject *Dtool_MappingWrapper_Values_repr(PyObject *self) {
614  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
615  nassertr(wrap, nullptr);
616 
617  PyObject *repr = PyObject_Repr(wrap->_self);
618  PyObject *result;
619 #if PY_MAJOR_VERSION >= 3
620  result = PyUnicode_FromFormat("<%s.values() of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
621 #else
622  result = PyString_FromFormat("<%s.values() of %s>", wrap->_name, PyString_AS_STRING(repr));
623 #endif
624  Py_DECREF(repr);
625  return result;
626 }
627 
628 static PyObject *Dtool_MappingWrapper_Values_getitem(PyObject *self, Py_ssize_t index) {
629  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
630  nassertr(wrap, nullptr);
631  nassertr(wrap->_keys._getitem_func, nullptr);
632 
633  PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
634  if (key != nullptr) {
635  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
636  Py_DECREF(key);
637  return value;
638  }
639  return nullptr;
640 }
641 
642 /**
643  * Implementation of property.values(...) that returns a view of the values.
644  */
645 static PyObject *Dtool_MappingWrapper_values(PyObject *self, PyObject *) {
646  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
647  nassertr(wrap, nullptr);
648  nassertr(wrap->_getitem_func, nullptr);
649 
650  if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
651  return Dtool_Raise_TypeError("property does not support values()");
652  }
653 
654  Dtool_MappingWrapper *values = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
655  if (values == nullptr) {
656  return PyErr_NoMemory();
657  }
658 
659  static PySequenceMethods seq_methods = {
660  Dtool_SequenceWrapper_length,
661  nullptr, // sq_concat
662  nullptr, // sq_repeat
663  Dtool_MappingWrapper_Values_getitem,
664  nullptr, // sq_slice
665  nullptr, // sq_ass_item
666  nullptr, // sq_ass_slice
667  Dtool_MappingWrapper_contains,
668  nullptr, // sq_inplace_concat
669  nullptr, // sq_inplace_repeat
670  };
671 
672  static PyTypeObject wrapper_type = {
673  PyVarObject_HEAD_INIT(nullptr, 0)
674  "sequence wrapper",
675  sizeof(Dtool_MappingWrapper),
676  0, // tp_itemsize
677  Dtool_WrapperBase_dealloc,
678  nullptr, // tp_print
679  nullptr, // tp_getattr
680  nullptr, // tp_setattr
681  nullptr, // tp_compare
682  Dtool_MappingWrapper_Values_repr,
683  nullptr, // tp_as_number
684  &seq_methods,
685  nullptr, // tp_as_mapping
686  nullptr, // tp_hash
687  nullptr, // tp_call
688  nullptr, // tp_str
689  PyObject_GenericGetAttr,
690  PyObject_GenericSetAttr,
691  nullptr, // tp_as_buffer
692  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
693  nullptr, // tp_doc
694  nullptr, // tp_traverse
695  nullptr, // tp_clear
696  nullptr, // tp_richcompare
697  0, // tp_weaklistoffset
698  PySeqIter_New,
699  nullptr, // tp_iternext
700  nullptr, // tp_methods
701  nullptr, // tp_members
702  nullptr, // tp_getset
703  nullptr, // tp_base
704  nullptr, // tp_dict
705  nullptr, // tp_descr_get
706  nullptr, // tp_descr_set
707  0, // tp_dictoffset
708  nullptr, // tp_init
709  PyType_GenericAlloc,
710  nullptr, // tp_new
711  PyObject_Del,
712  nullptr, // tp_is_gc
713  nullptr, // tp_bases
714  nullptr, // tp_mro
715  nullptr, // tp_cache
716  nullptr, // tp_subclasses
717  nullptr, // tp_weaklist
718  nullptr, // tp_del
719  };
720 
721  static bool registered = false;
722  if (!registered) {
723  registered = true;
724 
725  if (PyType_Ready(&wrapper_type) < 0) {
726  return nullptr;
727  }
728 
729  // If the collections.abc module is loaded, register this as a subclass.
730  _register_collection((PyTypeObject *)&wrapper_type, "ValuesView");
731  }
732 
733  (void)PyObject_INIT(values, &wrapper_type);
734  Py_XINCREF(wrap->_base._self);
735  values->_base._self = wrap->_base._self;
736  values->_base._name = wrap->_base._name;
737  values->_keys._len_func = wrap->_keys._len_func;
738  values->_keys._getitem_func = wrap->_keys._getitem_func;
739  values->_getitem_func = wrap->_getitem_func;
740  values->_setitem_func = nullptr;
741  return (PyObject *)values;
742 }
743 
744 /**
745  * This is returned by mapping.items().
746  */
747 static PyObject *Dtool_MappingWrapper_Items_repr(PyObject *self) {
748  Dtool_WrapperBase *wrap = (Dtool_WrapperBase *)self;
749  nassertr(wrap, nullptr);
750 
751  PyObject *repr = PyObject_Repr(wrap->_self);
752  PyObject *result;
753 #if PY_MAJOR_VERSION >= 3
754  result = PyUnicode_FromFormat("<%s.items() of %s>", wrap->_name, PyUnicode_AsUTF8(repr));
755 #else
756  result = PyString_FromFormat("<%s.items() of %s>", wrap->_name, PyString_AS_STRING(repr));
757 #endif
758  Py_DECREF(repr);
759  return result;
760 }
761 
762 static PyObject *Dtool_MappingWrapper_Items_getitem(PyObject *self, Py_ssize_t index) {
763  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
764  nassertr(wrap, nullptr);
765  nassertr(wrap->_keys._getitem_func, nullptr);
766 
767  PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
768  if (key != nullptr) {
769  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
770  if (value != nullptr) {
771  // PyTuple_SET_ITEM steals the reference.
772  PyObject *item = PyTuple_New(2);
773  PyTuple_SET_ITEM(item, 0, key);
774  PyTuple_SET_ITEM(item, 1, value);
775  return item;
776  } else {
777  Py_DECREF(key);
778  }
779  }
780  return nullptr;
781 }
782 
783 /**
784  * Implementation of property.items(...) that returns an iterable yielding a
785  * `(key, value)` tuple for every item.
786  */
787 static PyObject *Dtool_MappingWrapper_items(PyObject *self, PyObject *) {
788  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
789  nassertr(wrap, nullptr);
790  nassertr(wrap->_getitem_func, nullptr);
791 
792  if (wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
793  return Dtool_Raise_TypeError("property does not support items()");
794  }
795 
796  Dtool_MappingWrapper *items = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
797  if (items == nullptr) {
798  return PyErr_NoMemory();
799  }
800 
801  static PySequenceMethods seq_methods = {
802  Dtool_SequenceWrapper_length,
803  nullptr, // sq_concat
804  nullptr, // sq_repeat
805  Dtool_MappingWrapper_Items_getitem,
806  nullptr, // sq_slice
807  nullptr, // sq_ass_item
808  nullptr, // sq_ass_slice
809  Dtool_MappingWrapper_contains,
810  nullptr, // sq_inplace_concat
811  nullptr, // sq_inplace_repeat
812  };
813 
814  static PyTypeObject wrapper_type = {
815  PyVarObject_HEAD_INIT(nullptr, 0)
816  "sequence wrapper",
817  sizeof(Dtool_MappingWrapper),
818  0, // tp_itemsize
819  Dtool_WrapperBase_dealloc,
820  nullptr, // tp_print
821  nullptr, // tp_getattr
822  nullptr, // tp_setattr
823  nullptr, // tp_compare
824  Dtool_MappingWrapper_Items_repr,
825  nullptr, // tp_as_number
826  &seq_methods,
827  nullptr, // tp_as_mapping
828  nullptr, // tp_hash
829  nullptr, // tp_call
830  nullptr, // tp_str
831  PyObject_GenericGetAttr,
832  PyObject_GenericSetAttr,
833  nullptr, // tp_as_buffer
834  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
835  nullptr, // tp_doc
836  nullptr, // tp_traverse
837  nullptr, // tp_clear
838  nullptr, // tp_richcompare
839  0, // tp_weaklistoffset
840  PySeqIter_New,
841  nullptr, // tp_iternext
842  nullptr, // tp_methods
843  nullptr, // tp_members
844  nullptr, // tp_getset
845  nullptr, // tp_base
846  nullptr, // tp_dict
847  nullptr, // tp_descr_get
848  nullptr, // tp_descr_set
849  0, // tp_dictoffset
850  nullptr, // tp_init
851  PyType_GenericAlloc,
852  nullptr, // tp_new
853  PyObject_Del,
854  nullptr, // tp_is_gc
855  nullptr, // tp_bases
856  nullptr, // tp_mro
857  nullptr, // tp_cache
858  nullptr, // tp_subclasses
859  nullptr, // tp_weaklist
860  nullptr, // tp_del
861  };
862 
863  static bool registered = false;
864  if (!registered) {
865  registered = true;
866 
867  if (PyType_Ready(&wrapper_type) < 0) {
868  return nullptr;
869  }
870 
871  // If the collections.abc module is loaded, register this as a subclass.
872  _register_collection((PyTypeObject *)&wrapper_type, "MappingView");
873  }
874 
875  (void)PyObject_INIT(items, &wrapper_type);
876  Py_XINCREF(wrap->_base._self);
877  items->_base._self = wrap->_base._self;
878  items->_base._name = wrap->_base._name;
879  items->_keys._len_func = wrap->_keys._len_func;
880  items->_keys._getitem_func = wrap->_keys._getitem_func;
881  items->_getitem_func = wrap->_getitem_func;
882  items->_setitem_func = nullptr;
883  return (PyObject *)items;
884 }
885 
886 /**
887  * Implementation of `property[key] = value`
888  */
889 static int Dtool_MutableMappingWrapper_setitem(PyObject *self, PyObject *key, PyObject *value) {
890  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
891  nassertr(wrap->_setitem_func != nullptr, -1);
892  return wrap->_setitem_func(wrap->_base._self, key, value);
893 }
894 
895 /**
896  * Implementation of property.pop(key[,def=None]) which is the same as get()
897  * except that it also removes the element from the mapping.
898  */
899 static PyObject *Dtool_MutableMappingWrapper_pop(PyObject *self, PyObject *args) {
900  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
901  nassertr(wrap, nullptr);
902  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
903  return Dtool_Raise_TypeError("property does not support pop()");
904  }
905 
906  Py_ssize_t size = PyTuple_GET_SIZE(args);
907  if (size != 1 && size != 2) {
908  return PyErr_Format(PyExc_TypeError, "%s.pop() takes 1 or 2 arguments", wrap->_base._name);
909  }
910  PyObject *defvalue = Py_None;
911  if (size >= 2) {
912  defvalue = PyTuple_GET_ITEM(args, 1);
913  }
914 
915  PyObject *key = PyTuple_GET_ITEM(args, 0);
916  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
917  if (value != nullptr) {
918  // OK, now set unset this value.
919  if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) {
920  return value;
921  } else {
922  Py_DECREF(value);
923  return nullptr;
924  }
925  } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
926  PyErr_Restore(nullptr, nullptr, nullptr);
927  Py_INCREF(defvalue);
928  return defvalue;
929  } else {
930  return nullptr;
931  }
932 }
933 
934 /**
935  * Implementation of property.popitem() which returns and removes an arbitrary
936  * (key, value) pair from the mapping. Useful for destructive iteration.
937  */
938 static PyObject *Dtool_MutableMappingWrapper_popitem(PyObject *self, PyObject *) {
939  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
940  nassertr(wrap, nullptr);
941  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr ||
942  wrap->_keys._len_func == nullptr || wrap->_keys._getitem_func == nullptr) {
943  return Dtool_Raise_TypeError("property does not support popitem()");
944  }
945 
946  Py_ssize_t length = wrap->_keys._len_func(wrap->_base._self);
947  if (length < 1) {
948  return PyErr_Format(PyExc_KeyError, "%s is empty", wrap->_base._name);
949  }
950 
951  PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, length - 1);
952  if (key != nullptr) {
953  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
954  if (value != nullptr) {
955  // OK, now set unset this value.
956  if (wrap->_setitem_func(wrap->_base._self, key, nullptr) == 0) {
957  PyObject *item = PyTuple_New(2);
958  PyTuple_SET_ITEM(item, 0, key);
959  PyTuple_SET_ITEM(item, 1, value);
960  return item;
961  }
962  Py_DECREF(value);
963  }
964  }
965  return nullptr;
966 }
967 
968 /*
969  * Implementation of property.clear() which removes all elements in the
970  * mapping.
971  */
972 static PyObject *Dtool_MutableMappingWrapper_clear(PyObject *self, PyObject *) {
973  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
974  nassertr(wrap, nullptr);
975  Py_ssize_t index = 0;
976  if (wrap->_keys._len_func != nullptr && wrap->_keys._getitem_func != nullptr &&
977  wrap->_setitem_func != nullptr) {
978  index = wrap->_keys._len_func(wrap->_base._self);
979  } else {
980  return Dtool_Raise_TypeError("property does not support clear()");
981  }
982 
983  // Iterate through the items, invoking the delete function for each. We do
984  // this in reverse order, which may be more efficient.
985  while (index > 0) {
986  --index;
987  PyObject *key = wrap->_keys._getitem_func(wrap->_base._self, index);
988  if (key != nullptr) {
989  int result = wrap->_setitem_func(wrap->_base._self, key, nullptr);
990  Py_DECREF(key);
991  if (result != 0) {
992  return nullptr;
993  }
994  }
995  }
996  Py_INCREF(Py_None);
997  return Py_None;
998 }
999 
1000 /**
1001  * Implementation of property.setdefault(key[,def=None]) which is the same as
1002  * get() except that it also writes the default value back to the mapping if
1003  * the key was not found is missing.
1004  */
1005 static PyObject *Dtool_MutableMappingWrapper_setdefault(PyObject *self, PyObject *args) {
1006  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
1007  nassertr(wrap, nullptr);
1008 
1009  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
1010  return Dtool_Raise_TypeError("property does not support setdefault()");
1011  }
1012 
1013  Py_ssize_t size = PyTuple_GET_SIZE(args);
1014  if (size != 1 && size != 2) {
1015  return PyErr_Format(PyExc_TypeError, "%s.setdefault() takes 1 or 2 arguments", wrap->_base._name);
1016  }
1017  PyObject *defvalue = Py_None;
1018  if (size >= 2) {
1019  defvalue = PyTuple_GET_ITEM(args, 1);
1020  }
1021  PyObject *key = PyTuple_GET_ITEM(args, 0);
1022  PyObject *value = wrap->_getitem_func(wrap->_base._self, key);
1023  if (value != nullptr) {
1024  return value;
1025  } else if (_PyErr_OCCURRED() == PyExc_KeyError) {
1026  PyErr_Restore(nullptr, nullptr, nullptr);
1027  if (wrap->_setitem_func(wrap->_base._self, key, defvalue) == 0) {
1028  Py_INCREF(defvalue);
1029  return defvalue;
1030  }
1031  }
1032  return nullptr;
1033 }
1034 
1035 /**
1036  * Implementation of property.update(...) which sets multiple values in one
1037  * go. It accepts either a single dictionary or keyword arguments, not both.
1038  */
1039 static PyObject *Dtool_MutableMappingWrapper_update(PyObject *self, PyObject *args, PyObject *kwargs) {
1040  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)self;
1041  nassertr(wrap, nullptr);
1042 
1043  if (wrap->_getitem_func == nullptr || wrap->_setitem_func == nullptr) {
1044  return Dtool_Raise_TypeError("property does not support update()");
1045  }
1046 
1047  // We accept either a dict argument or keyword arguments, but not both.
1048  PyObject *dict;
1049  switch (PyTuple_GET_SIZE(args)) {
1050  case 0:
1051  if (kwargs == nullptr) {
1052  // This is legal.
1053  Py_INCREF(Py_None);
1054  return Py_None;
1055  }
1056  dict = kwargs;
1057  break;
1058  case 1:
1059  if (PyDict_Check(PyTuple_GET_ITEM(args, 0)) && (kwargs == nullptr || Py_SIZE(kwargs) == 0)) {
1060  dict = PyTuple_GET_ITEM(args, 0);
1061  break;
1062  }
1063  // Fall through
1064  default:
1065  return PyErr_Format(PyExc_TypeError, "%s.update() takes either a dict argument or keyword arguments", wrap->_base._name);
1066  }
1067 
1068  PyObject *key, *value;
1069  Py_ssize_t pos = 0;
1070  while (PyDict_Next(dict, &pos, &key, &value)) {
1071  if (wrap->_setitem_func(wrap->_base._self, key, value) != 0) {
1072  return nullptr;
1073  }
1074  }
1075  Py_INCREF(Py_None);
1076  return Py_None;
1077 }
1078 
1079 /**
1080  * This variant defines only a generator interface.
1081  */
1082 static PyObject *Dtool_GeneratorWrapper_iternext(PyObject *self) {
1083  Dtool_GeneratorWrapper *wrap = (Dtool_GeneratorWrapper *)self;
1084  nassertr(wrap, nullptr);
1085  nassertr(wrap->_iternext_func, nullptr);
1086  return wrap->_iternext_func(wrap->_base._self);
1087 }
1088 
1089 /**
1090  * This is a variant of the Python getset mechanism that permits static
1091  * properties.
1092  */
1093 static void
1094 Dtool_StaticProperty_dealloc(PyDescrObject *descr) {
1095 #if PY_VERSION_HEX >= 0x03080000
1096  PyObject_GC_UnTrack(descr);
1097 #else
1098  _PyObject_GC_UNTRACK(descr);
1099 #endif
1100  Py_XDECREF(descr->d_type);
1101  Py_XDECREF(descr->d_name);
1102 //#if PY_MAJOR_VERSION >= 3
1103 // Py_XDECREF(descr->d_qualname);
1104 //#endif
1105  PyObject_GC_Del(descr);
1106 }
1107 
1108 static PyObject *
1109 Dtool_StaticProperty_repr(PyDescrObject *descr, const char *format) {
1110 #if PY_MAJOR_VERSION >= 3
1111  return PyUnicode_FromFormat("<attribute '%s' of '%s'>",
1112  PyUnicode_AsUTF8(descr->d_name),
1113  descr->d_type->tp_name);
1114 #else
1115  return PyString_FromFormat("<attribute '%s' of '%s'>",
1116  PyString_AS_STRING(descr->d_name),
1117  descr->d_type->tp_name);
1118 #endif
1119 }
1120 
1121 static int
1122 Dtool_StaticProperty_traverse(PyObject *self, visitproc visit, void *arg) {
1123  PyDescrObject *descr = (PyDescrObject *)self;
1124  Py_VISIT(descr->d_type);
1125  return 0;
1126 }
1127 
1128 static PyObject *
1129 Dtool_StaticProperty_get(PyGetSetDescrObject *descr, PyObject *obj, PyObject *type) {
1130  if (descr->d_getset->get != nullptr) {
1131  return descr->d_getset->get(obj, descr->d_getset->closure);
1132  } else {
1133  return PyErr_Format(PyExc_AttributeError,
1134  "attribute '%s' of type '%.100s' is not readable",
1135 #if PY_MAJOR_VERSION >= 3
1136  PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name),
1137 #else
1138  PyString_AS_STRING(((PyDescrObject *)descr)->d_name),
1139 #endif
1140  ((PyDescrObject *)descr)->d_type->tp_name);
1141  }
1142 }
1143 
1144 static int
1145 Dtool_StaticProperty_set(PyGetSetDescrObject *descr, PyObject *obj, PyObject *value) {
1146  if (descr->d_getset->set != nullptr) {
1147  return descr->d_getset->set(obj, value, descr->d_getset->closure);
1148  } else {
1149  PyErr_Format(PyExc_AttributeError,
1150  "attribute '%s' of type '%.100s' is not writable",
1151 #if PY_MAJOR_VERSION >= 3
1152  PyUnicode_AsUTF8(((PyDescrObject *)descr)->d_name),
1153 #else
1154  PyString_AS_STRING(((PyDescrObject *)descr)->d_name),
1155 #endif
1156  ((PyDescrObject *)descr)->d_type->tp_name);
1157  return -1;
1158  }
1159 }
1160 
1161 /**
1162  * This wraps around a property that exposes a sequence interface.
1163  */
1164 Dtool_SequenceWrapper *Dtool_NewSequenceWrapper(PyObject *self, const char *name) {
1165  Dtool_SequenceWrapper *wrap = (Dtool_SequenceWrapper *)PyObject_MALLOC(sizeof(Dtool_SequenceWrapper));
1166  if (wrap == nullptr) {
1167  return (Dtool_SequenceWrapper *)PyErr_NoMemory();
1168  }
1169 
1170  static PySequenceMethods seq_methods = {
1171  Dtool_SequenceWrapper_length,
1172  nullptr, // sq_concat
1173  nullptr, // sq_repeat
1174  Dtool_SequenceWrapper_getitem,
1175  nullptr, // sq_slice
1176  nullptr, // sq_ass_item
1177  nullptr, // sq_ass_slice
1178  Dtool_SequenceWrapper_contains,
1179  nullptr, // sq_inplace_concat
1180  nullptr, // sq_inplace_repeat
1181  };
1182 
1183  static PyMethodDef methods[] = {
1184  {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr},
1185  {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr},
1186  {nullptr, nullptr, 0, nullptr}
1187  };
1188 
1189  static PyTypeObject wrapper_type = {
1190  PyVarObject_HEAD_INIT(nullptr, 0)
1191  "sequence wrapper",
1192  sizeof(Dtool_SequenceWrapper),
1193  0, // tp_itemsize
1194  Dtool_WrapperBase_dealloc,
1195  nullptr, // tp_print
1196  nullptr, // tp_getattr
1197  nullptr, // tp_setattr
1198  nullptr, // tp_compare
1199  Dtool_SequenceWrapper_repr,
1200  nullptr, // tp_as_number
1201  &seq_methods,
1202  nullptr, // tp_as_mapping
1203  nullptr, // tp_hash
1204  nullptr, // tp_call
1205  nullptr, // tp_str
1206  PyObject_GenericGetAttr,
1207  PyObject_GenericSetAttr,
1208  nullptr, // tp_as_buffer
1209  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1210  nullptr, // tp_doc
1211  nullptr, // tp_traverse
1212  nullptr, // tp_clear
1213  nullptr, // tp_richcompare
1214  0, // tp_weaklistoffset
1215  PySeqIter_New,
1216  nullptr, // tp_iternext
1217  methods,
1218  nullptr, // tp_members
1219  nullptr, // tp_getset
1220  nullptr, // tp_base
1221  nullptr, // tp_dict
1222  nullptr, // tp_descr_get
1223  nullptr, // tp_descr_set
1224  0, // tp_dictoffset
1225  nullptr, // tp_init
1226  PyType_GenericAlloc,
1227  nullptr, // tp_new
1228  PyObject_Del,
1229  nullptr, // tp_is_gc
1230  nullptr, // tp_bases
1231  nullptr, // tp_mro
1232  nullptr, // tp_cache
1233  nullptr, // tp_subclasses
1234  nullptr, // tp_weaklist
1235  nullptr, // tp_del
1236  };
1237 
1238  static bool registered = false;
1239  if (!registered) {
1240  registered = true;
1241 
1242  if (PyType_Ready(&wrapper_type) < 0) {
1243  return nullptr;
1244  }
1245 
1246  // If the collections.abc module is loaded, register this as a subclass.
1247  _register_collection((PyTypeObject *)&wrapper_type, "Sequence");
1248  }
1249 
1250  (void)PyObject_INIT(wrap, &wrapper_type);
1251  Py_XINCREF(self);
1252  wrap->_base._self = self;
1253  wrap->_base._name = name;
1254  wrap->_len_func = nullptr;
1255  wrap->_getitem_func = nullptr;
1256  return wrap;
1257 }
1258 
1259 /**
1260  * This wraps around a property that exposes a mutable sequence interface.
1261  */
1262 Dtool_MutableSequenceWrapper *Dtool_NewMutableSequenceWrapper(PyObject *self, const char *name) {
1263  Dtool_MutableSequenceWrapper *wrap = (Dtool_MutableSequenceWrapper *)PyObject_MALLOC(sizeof(Dtool_MutableSequenceWrapper));
1264  if (wrap == nullptr) {
1265  return (Dtool_MutableSequenceWrapper *)PyErr_NoMemory();
1266  }
1267 
1268  static PySequenceMethods seq_methods = {
1269  Dtool_SequenceWrapper_length,
1270  nullptr, // sq_concat
1271  nullptr, // sq_repeat
1272  Dtool_SequenceWrapper_getitem,
1273  nullptr, // sq_slice
1274  Dtool_MutableSequenceWrapper_setitem,
1275  nullptr, // sq_ass_slice
1276  Dtool_SequenceWrapper_contains,
1277  Dtool_MutableSequenceWrapper_extend,
1278  nullptr, // sq_inplace_repeat
1279  };
1280 
1281  static PyMethodDef methods[] = {
1282  {"index", &Dtool_SequenceWrapper_index, METH_O, nullptr},
1283  {"count", &Dtool_SequenceWrapper_count, METH_O, nullptr},
1284  {"clear", &Dtool_MutableSequenceWrapper_clear, METH_NOARGS, nullptr},
1285  {"pop", &Dtool_MutableSequenceWrapper_pop, METH_VARARGS, nullptr},
1286  {"remove", &Dtool_MutableSequenceWrapper_remove, METH_O, nullptr},
1287  {"append", &Dtool_MutableSequenceWrapper_append, METH_O, nullptr},
1288  {"insert", &Dtool_MutableSequenceWrapper_insert, METH_VARARGS, nullptr},
1289  {"extend", &Dtool_MutableSequenceWrapper_extend, METH_O, nullptr},
1290  {nullptr, nullptr, 0, nullptr}
1291  };
1292 
1293  static PyTypeObject wrapper_type = {
1294  PyVarObject_HEAD_INIT(nullptr, 0)
1295  "sequence wrapper",
1296  sizeof(Dtool_MutableSequenceWrapper),
1297  0, // tp_itemsize
1298  Dtool_WrapperBase_dealloc,
1299  nullptr, // tp_print
1300  nullptr, // tp_getattr
1301  nullptr, // tp_setattr
1302  nullptr, // tp_compare
1303  Dtool_SequenceWrapper_repr,
1304  nullptr, // tp_as_number
1305  &seq_methods,
1306  nullptr, // tp_as_mapping
1307  nullptr, // tp_hash
1308  nullptr, // tp_call
1309  nullptr, // tp_str
1310  PyObject_GenericGetAttr,
1311  PyObject_GenericSetAttr,
1312  nullptr, // tp_as_buffer
1313  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1314  nullptr, // tp_doc
1315  nullptr, // tp_traverse
1316  nullptr, // tp_clear
1317  nullptr, // tp_richcompare
1318  0, // tp_weaklistoffset
1319  PySeqIter_New,
1320  nullptr, // tp_iternext
1321  methods,
1322  nullptr, // tp_members
1323  nullptr, // tp_getset
1324  nullptr, // tp_base
1325  nullptr, // tp_dict
1326  nullptr, // tp_descr_get
1327  nullptr, // tp_descr_set
1328  0, // tp_dictoffset
1329  nullptr, // tp_init
1330  PyType_GenericAlloc,
1331  nullptr, // tp_new
1332  PyObject_Del,
1333  nullptr, // tp_is_gc
1334  nullptr, // tp_bases
1335  nullptr, // tp_mro
1336  nullptr, // tp_cache
1337  nullptr, // tp_subclasses
1338  nullptr, // tp_weaklist
1339  nullptr, // tp_del
1340  };
1341 
1342  static bool registered = false;
1343  if (!registered) {
1344  registered = true;
1345 
1346  if (PyType_Ready(&wrapper_type) < 0) {
1347  return nullptr;
1348  }
1349 
1350  // If the collections.abc module is loaded, register this as a subclass.
1351  _register_collection((PyTypeObject *)&wrapper_type, "MutableSequence");
1352  }
1353 
1354  (void)PyObject_INIT(wrap, &wrapper_type);
1355  Py_XINCREF(self);
1356  wrap->_base._self = self;
1357  wrap->_base._name = name;
1358  wrap->_len_func = nullptr;
1359  wrap->_getitem_func = nullptr;
1360  wrap->_setitem_func = nullptr;
1361  wrap->_insert_func = nullptr;
1362  return wrap;
1363 }
1364 
1365 /**
1366  * This wraps around a mapping interface, with getitem function.
1367  */
1368 Dtool_MappingWrapper *Dtool_NewMappingWrapper(PyObject *self, const char *name) {
1369  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
1370  if (wrap == nullptr) {
1371  return (Dtool_MappingWrapper *)PyErr_NoMemory();
1372  }
1373 
1374  static PySequenceMethods seq_methods = {
1375  Dtool_SequenceWrapper_length,
1376  nullptr, // sq_concat
1377  nullptr, // sq_repeat
1378  nullptr, // sq_item
1379  nullptr, // sq_slice
1380  nullptr, // sq_ass_item
1381  nullptr, // sq_ass_slice
1382  Dtool_MappingWrapper_contains,
1383  nullptr, // sq_inplace_concat
1384  nullptr, // sq_inplace_repeat
1385  };
1386 
1387  static PyMappingMethods map_methods = {
1388  Dtool_SequenceWrapper_length,
1389  Dtool_MappingWrapper_getitem,
1390  nullptr, // mp_ass_subscript
1391  };
1392 
1393  static PyMethodDef methods[] = {
1394  {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr},
1395  {"keys", &Dtool_MappingWrapper_keys, METH_NOARGS, nullptr},
1396  {"values", &Dtool_MappingWrapper_values, METH_NOARGS, nullptr},
1397  {"items", &Dtool_MappingWrapper_items, METH_NOARGS, nullptr},
1398  {nullptr, nullptr, 0, nullptr}
1399  };
1400 
1401  static PyTypeObject wrapper_type = {
1402  PyVarObject_HEAD_INIT(nullptr, 0)
1403  "mapping wrapper",
1404  sizeof(Dtool_MappingWrapper),
1405  0, // tp_itemsize
1406  Dtool_WrapperBase_dealloc,
1407  nullptr, // tp_print
1408  nullptr, // tp_getattr
1409  nullptr, // tp_setattr
1410  nullptr, // tp_compare
1411  Dtool_WrapperBase_repr,
1412  nullptr, // tp_as_number
1413  &seq_methods,
1414  &map_methods,
1415  nullptr, // tp_hash
1416  nullptr, // tp_call
1417  nullptr, // tp_str
1418  PyObject_GenericGetAttr,
1419  PyObject_GenericSetAttr,
1420  nullptr, // tp_as_buffer
1421  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1422  nullptr, // tp_doc
1423  nullptr, // tp_traverse
1424  nullptr, // tp_clear
1425  nullptr, // tp_richcompare
1426  0, // tp_weaklistoffset
1427  Dtool_MappingWrapper_iter,
1428  nullptr, // tp_iternext
1429  methods,
1430  nullptr, // tp_members
1431  nullptr, // tp_getset
1432  nullptr, // tp_base
1433  nullptr, // tp_dict
1434  nullptr, // tp_descr_get
1435  nullptr, // tp_descr_set
1436  0, // tp_dictoffset
1437  nullptr, // tp_init
1438  PyType_GenericAlloc,
1439  nullptr, // tp_new
1440  PyObject_Del,
1441  nullptr, // tp_is_gc
1442  nullptr, // tp_bases
1443  nullptr, // tp_mro
1444  nullptr, // tp_cache
1445  nullptr, // tp_subclasses
1446  nullptr, // tp_weaklist
1447  nullptr, // tp_del
1448  };
1449 
1450  static bool registered = false;
1451  if (!registered) {
1452  registered = true;
1453 
1454  if (PyType_Ready(&wrapper_type) < 0) {
1455  return nullptr;
1456  }
1457 
1458  // If the collections.abc module is loaded, register this as a subclass.
1459  _register_collection((PyTypeObject *)&wrapper_type, "Mapping");
1460  }
1461 
1462  (void)PyObject_INIT(wrap, &wrapper_type);
1463  Py_XINCREF(self);
1464  wrap->_base._self = self;
1465  wrap->_base._name = name;
1466  wrap->_keys._len_func = nullptr;
1467  wrap->_keys._getitem_func = nullptr;
1468  wrap->_getitem_func = nullptr;
1469  wrap->_setitem_func = nullptr;
1470  return wrap;
1471 }
1472 
1473 /**
1474  * This wraps around a mapping interface, with getitem/setitem functions.
1475  */
1476 Dtool_MappingWrapper *Dtool_NewMutableMappingWrapper(PyObject *self, const char *name) {
1477  Dtool_MappingWrapper *wrap = (Dtool_MappingWrapper *)PyObject_MALLOC(sizeof(Dtool_MappingWrapper));
1478  if (wrap == nullptr) {
1479  return (Dtool_MappingWrapper *)PyErr_NoMemory();
1480  }
1481 
1482  static PySequenceMethods seq_methods = {
1483  Dtool_SequenceWrapper_length,
1484  nullptr, // sq_concat
1485  nullptr, // sq_repeat
1486  nullptr, // sq_item
1487  nullptr, // sq_slice
1488  nullptr, // sq_ass_item
1489  nullptr, // sq_ass_slice
1490  Dtool_MappingWrapper_contains,
1491  nullptr, // sq_inplace_concat
1492  nullptr, // sq_inplace_repeat
1493  };
1494 
1495  static PyMappingMethods map_methods = {
1496  Dtool_SequenceWrapper_length,
1497  Dtool_MappingWrapper_getitem,
1498  Dtool_MutableMappingWrapper_setitem,
1499  };
1500 
1501  static PyMethodDef methods[] = {
1502  {"get", &Dtool_MappingWrapper_get, METH_VARARGS, nullptr},
1503  {"pop", &Dtool_MutableMappingWrapper_pop, METH_VARARGS, nullptr},
1504  {"popitem", &Dtool_MutableMappingWrapper_popitem, METH_NOARGS, nullptr},
1505  {"clear", &Dtool_MutableMappingWrapper_clear, METH_VARARGS, nullptr},
1506  {"setdefault", &Dtool_MutableMappingWrapper_setdefault, METH_VARARGS, nullptr},
1507  {"update", (PyCFunction) &Dtool_MutableMappingWrapper_update, METH_VARARGS | METH_KEYWORDS, nullptr},
1508  {"keys", &Dtool_MappingWrapper_keys, METH_NOARGS, nullptr},
1509  {"values", &Dtool_MappingWrapper_values, METH_NOARGS, nullptr},
1510  {"items", &Dtool_MappingWrapper_items, METH_NOARGS, nullptr},
1511  {nullptr, nullptr, 0, nullptr}
1512  };
1513 
1514  static PyTypeObject wrapper_type = {
1515  PyVarObject_HEAD_INIT(nullptr, 0)
1516  "mapping wrapper",
1517  sizeof(Dtool_MappingWrapper),
1518  0, // tp_itemsize
1519  Dtool_WrapperBase_dealloc,
1520  nullptr, // tp_print
1521  nullptr, // tp_getattr
1522  nullptr, // tp_setattr
1523  nullptr, // tp_compare
1524  Dtool_WrapperBase_repr,
1525  nullptr, // tp_as_number
1526  &seq_methods,
1527  &map_methods,
1528  nullptr, // tp_hash
1529  nullptr, // tp_call
1530  nullptr, // tp_str
1531  PyObject_GenericGetAttr,
1532  PyObject_GenericSetAttr,
1533  nullptr, // tp_as_buffer
1534  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1535  nullptr, // tp_doc
1536  nullptr, // tp_traverse
1537  nullptr, // tp_clear
1538  nullptr, // tp_richcompare
1539  0, // tp_weaklistoffset
1540  Dtool_MappingWrapper_iter,
1541  nullptr, // tp_iternext
1542  methods,
1543  nullptr, // tp_members
1544  nullptr, // tp_getset
1545  nullptr, // tp_base
1546  nullptr, // tp_dict
1547  nullptr, // tp_descr_get
1548  nullptr, // tp_descr_set
1549  0, // tp_dictoffset
1550  nullptr, // tp_init
1551  PyType_GenericAlloc,
1552  nullptr, // tp_new
1553  PyObject_Del,
1554  nullptr, // tp_is_gc
1555  nullptr, // tp_bases
1556  nullptr, // tp_mro
1557  nullptr, // tp_cache
1558  nullptr, // tp_subclasses
1559  nullptr, // tp_weaklist
1560  nullptr, // tp_del
1561  };
1562 
1563  static bool registered = false;
1564  if (!registered) {
1565  registered = true;
1566 
1567  if (PyType_Ready(&wrapper_type) < 0) {
1568  return nullptr;
1569  }
1570 
1571  // If the collections.abc module is loaded, register this as a subclass.
1572  _register_collection((PyTypeObject *)&wrapper_type, "MutableMapping");
1573  }
1574 
1575  (void)PyObject_INIT(wrap, &wrapper_type);
1576  Py_XINCREF(self);
1577  wrap->_base._self = self;
1578  wrap->_base._name = name;
1579  wrap->_keys._len_func = nullptr;
1580  wrap->_keys._getitem_func = nullptr;
1581  wrap->_getitem_func = nullptr;
1582  wrap->_setitem_func = nullptr;
1583  return wrap;
1584 }
1585 
1586 /**
1587  * Creates a generator that invokes a given function with the given self arg.
1588  */
1589 PyObject *
1590 Dtool_NewGenerator(PyObject *self, iternextfunc gen_next) {
1591  static PyTypeObject wrapper_type = {
1592  PyVarObject_HEAD_INIT(nullptr, 0)
1593  "generator wrapper",
1594  sizeof(Dtool_GeneratorWrapper),
1595  0, // tp_itemsize
1596  Dtool_WrapperBase_dealloc,
1597  nullptr, // tp_print
1598  nullptr, // tp_getattr
1599  nullptr, // tp_setattr
1600  nullptr, // tp_compare
1601  nullptr, // tp_repr
1602  nullptr, // tp_as_number
1603  nullptr, // tp_as_sequence
1604  nullptr, // tp_as_mapping
1605  nullptr, // tp_hash
1606  nullptr, // tp_call
1607  nullptr, // tp_str
1608  PyObject_GenericGetAttr,
1609  PyObject_GenericSetAttr,
1610  nullptr, // tp_as_buffer
1611  Py_TPFLAGS_DEFAULT | Py_TPFLAGS_CHECKTYPES,
1612  nullptr, // tp_doc
1613  nullptr, // tp_traverse
1614  nullptr, // tp_clear
1615  nullptr, // tp_richcompare
1616  0, // tp_weaklistoffset
1617  PyObject_SelfIter,
1618  Dtool_GeneratorWrapper_iternext,
1619  nullptr, // tp_methods
1620  nullptr, // tp_members
1621  nullptr, // tp_getset
1622  nullptr, // tp_base
1623  nullptr, // tp_dict
1624  nullptr, // tp_descr_get
1625  nullptr, // tp_descr_set
1626  0, // tp_dictoffset
1627  nullptr, // tp_init
1628  PyType_GenericAlloc,
1629  nullptr, // tp_new
1630  PyObject_Del,
1631  nullptr, // tp_is_gc
1632  nullptr, // tp_bases
1633  nullptr, // tp_mro
1634  nullptr, // tp_cache
1635  nullptr, // tp_subclasses
1636  nullptr, // tp_weaklist
1637  nullptr, // tp_del
1638  };
1639 
1640  if (PyType_Ready(&wrapper_type) < 0) {
1641  return nullptr;
1642  }
1643 
1644  Dtool_GeneratorWrapper *gen;
1645  gen = (Dtool_GeneratorWrapper *)PyType_GenericAlloc(&wrapper_type, 0);
1646  if (gen != nullptr) {
1647  Py_INCREF(self);
1648  gen->_base._self = self;
1649  gen->_iternext_func = gen_next;
1650  }
1651  return (PyObject *)gen;
1652 }
1653 
1654 /**
1655  * This is a variant of the Python getset mechanism that permits static
1656  * properties.
1657  */
1658 PyObject *
1659 Dtool_NewStaticProperty(PyTypeObject *type, const PyGetSetDef *getset) {
1660  static PyTypeObject wrapper_type = {
1661  PyVarObject_HEAD_INIT(&PyType_Type, 0)
1662  "getset_descriptor",
1663  sizeof(PyGetSetDescrObject),
1664  0, // tp_itemsize
1665  (destructor)Dtool_StaticProperty_dealloc,
1666  nullptr, // tp_print
1667  nullptr, // tp_getattr
1668  nullptr, // tp_setattr
1669  nullptr, // tp_reserved
1670  (reprfunc)Dtool_StaticProperty_repr,
1671  nullptr, // tp_as_number
1672  nullptr, // tp_as_sequence
1673  nullptr, // tp_as_mapping
1674  nullptr, // tp_hash
1675  nullptr, // tp_call
1676  nullptr, // tp_str
1677  PyObject_GenericGetAttr,
1678  nullptr, // tp_setattro
1679  nullptr, // tp_as_buffer
1680  Py_TPFLAGS_DEFAULT,
1681  nullptr, // tp_doc
1682  Dtool_StaticProperty_traverse,
1683  nullptr, // tp_clear
1684  nullptr, // tp_richcompare
1685  0, // tp_weaklistoffset
1686  nullptr, // tp_iter
1687  nullptr, // tp_iternext
1688  nullptr, // tp_methods
1689  nullptr, // tp_members
1690  nullptr, // tp_getset
1691  nullptr, // tp_base
1692  nullptr, // tp_dict
1693  (descrgetfunc)Dtool_StaticProperty_get,
1694  (descrsetfunc)Dtool_StaticProperty_set,
1695  0, // tp_dictoffset
1696  nullptr, // tp_init
1697  nullptr, // tp_alloc
1698  nullptr, // tp_new
1699  nullptr, // tp_del
1700  nullptr, // tp_is_gc
1701  nullptr, // tp_bases
1702  nullptr, // tp_mro
1703  nullptr, // tp_cache
1704  nullptr, // tp_subclasses
1705  nullptr, // tp_weaklist
1706  nullptr, // tp_del
1707  };
1708 
1709  if (PyType_Ready(&wrapper_type) < 0) {
1710  return nullptr;
1711  }
1712 
1713  PyGetSetDescrObject *descr;
1714  descr = (PyGetSetDescrObject *)PyType_GenericAlloc(&wrapper_type, 0);
1715  if (descr != nullptr) {
1716  Py_XINCREF(type);
1717  descr->d_getset = (PyGetSetDef *)getset;
1718 #if PY_MAJOR_VERSION >= 3
1719  descr->d_common.d_type = type;
1720  descr->d_common.d_name = PyUnicode_InternFromString(getset->name);
1721 #if PY_VERSION_HEX >= 0x03030000
1722  descr->d_common.d_qualname = nullptr;
1723 #endif
1724 #else
1725  descr->d_type = type;
1726  descr->d_name = PyString_InternFromString(getset->name);
1727 #endif
1728  }
1729  return (PyObject *)descr;
1730 }
1731 
1732 #endif // HAVE_PYTHON
PyObject * Dtool_WrapValue(int value)
The following functions wrap an arbitrary C++ value into a PyObject.
Definition: py_panda.I:183