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