Panda3D
py_panda.cxx
1 // Filename: py_panda.cxx
2 // Created by: drose (04Jul05)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 #include "py_panda.h"
16 #include "config_interrogatedb.h"
17 
18 #ifdef HAVE_PYTHON
19 
20 PyMemberDef standard_type_members[] = {
21  {(char *)"this", (sizeof(void*) == sizeof(int)) ? T_UINT : T_ULONGLONG, offsetof(Dtool_PyInstDef, _ptr_to_object), READONLY, (char *)"C++ 'this' pointer, if any"},
22  {(char *)"this_ownership", T_BOOL, offsetof(Dtool_PyInstDef, _memory_rules), READONLY, (char *)"C++ 'this' ownership rules"},
23  {(char *)"this_const", T_BOOL, offsetof(Dtool_PyInstDef, _is_const), READONLY, (char *)"C++ 'this' const flag"},
24 // {(char *)"this_signature", T_INT, offsetof(Dtool_PyInstDef, _signature), READONLY, (char *)"A type check signature"},
25  {(char *)"this_metatype", T_OBJECT, offsetof(Dtool_PyInstDef, _My_Type), READONLY, (char *)"The dtool meta object"},
26  {NULL} /* Sentinel */
27 };
28 
29 ////////////////////////////////////////////////////////////////////
30 // Function: DtoolCanThisBeAPandaInstance
31 // Description: Given a valid (non-NULL) PyObject, does a simple
32 // check to see if it might be an instance of a Panda
33 // type. It does this using a signature that is
34 // encoded on each instance.
35 ////////////////////////////////////////////////////////////////////
36 bool DtoolCanThisBeAPandaInstance(PyObject *self) {
37  // simple sanity check for the class type..size.. will stop basic foobars..
38  // It is arguably better to use something like this:
39  // PyType_IsSubtype(Py_TYPE(self), &Dtool_DTOOL_SUPER_BASE._PyType)
40  // ...but probably not as fast.
41  if (Py_TYPE(self)->tp_basicsize >= (int)sizeof(Dtool_PyInstDef)) {
42  Dtool_PyInstDef *pyself = (Dtool_PyInstDef *) self;
43  if (pyself->_signature == PY_PANDA_SIGNATURE) {
44  return true;
45  }
46  }
47  return false;
48 }
49 
50 ////////////////////////////////////////////////////////////////////////
51 // Function : DTOOL_Call_ExtractThisPointerForType
52 //
53 // These are the wrappers that allow for down and upcast from type ..
54 // needed by the Dtool py interface.. Be very careful if you muck with these
55 // as the generated code depends on how this is set up..
56 ////////////////////////////////////////////////////////////////////////
57 void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer) {
58  if (DtoolCanThisBeAPandaInstance(self)) {
59  *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, classdef);
60  } else {
61  *answer = NULL;
62  }
63 }
64 
65 ////////////////////////////////////////////////////////////////////
66 // Function: Dtool_Call_ExtractThisPointer
67 // Description: This is a support function for the Python bindings:
68 // it extracts the underlying C++ pointer of the given
69 // type for a given Python object. If it was of the
70 // wrong type, raises an AttributeError.
71 ////////////////////////////////////////////////////////////////////
72 bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer) {
73  if (self == NULL || !DtoolCanThisBeAPandaInstance(self) || ((Dtool_PyInstDef *)self)->_ptr_to_object == NULL) {
74  Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
75  return false;
76  }
77 
78  *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, &classdef);
79  return true;
80 }
81 
82 ////////////////////////////////////////////////////////////////////
83 // Function: Dtool_Call_ExtractThisPointer_NonConst
84 // Description: The same thing as Dtool_Call_ExtractThisPointer,
85 // except that it performs the additional check that
86 // the pointer is a non-const pointer. This is called
87 // by function wrappers for functions of which all
88 // overloads are non-const, and saves a bit of code.
89 //
90 // The extra method_name argument is used in formatting
91 // the error message.
92 ////////////////////////////////////////////////////////////////////
93 bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
94  void **answer, const char *method_name) {
95 
96  if (self == NULL || !DtoolCanThisBeAPandaInstance(self) || ((Dtool_PyInstDef *)self)->_ptr_to_object == NULL) {
97  Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
98  return false;
99  }
100 
101  if (((Dtool_PyInstDef *)self)->_is_const) {
102  // All overloads of this function are non-const.
103  PyErr_Format(PyExc_TypeError,
104  "Cannot call %s() on a const object.",
105  method_name);
106  return false;
107  }
108 
109  *answer = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, &classdef);
110  return true;
111 }
112 
113 ////////////////////////////////////////////////////////////////////
114 // Function: DTOOL_Call_GetPointerThisClass
115 // Description: Extracts the C++ pointer for an object, given its
116 // Python wrapper object, for passing as the parameter
117 // to a C++ function.
118 //
119 // self is the Python wrapper object in question.
120 //
121 // classdef is the Python class wrapper for the C++
122 // class in which the this pointer should be returned.
123 // (This may require an upcast operation, if self is not
124 // already an instance of classdef.)
125 //
126 // param and function_name are used for error reporting
127 // only, and describe the particular function and
128 // parameter index for this parameter.
129 //
130 // const_ok is true if the function is declared const
131 // and can therefore be called with either a const or
132 // non-const "this" pointer, or false if the function is
133 // declared non-const, and can therefore be called with
134 // only a non-const "this" pointer.
135 //
136 // The return value is the C++ pointer that was
137 // extracted, or NULL if there was a problem (in which
138 // case the Python exception state will have been set).
139 ////////////////////////////////////////////////////////////////////
140 void *
141 DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef,
142  int param, const string &function_name, bool const_ok,
143  bool report_errors) {
144  //if (PyErr_Occurred()) {
145  // return NULL;
146  //}
147  if (self == NULL) {
148  if (report_errors) {
149  return Dtool_Raise_TypeError("self is NULL");
150  }
151  return NULL;
152  }
153 
154  if (DtoolCanThisBeAPandaInstance(self)) {
155  void *result = ((Dtool_PyInstDef *)self)->_My_Type->_Dtool_UpcastInterface(self, classdef);
156 
157  if (result != NULL) {
158  if (const_ok || !((Dtool_PyInstDef *)self)->_is_const) {
159  return result;
160  }
161 
162  if (report_errors) {
163  return PyErr_Format(PyExc_TypeError,
164  "%s() argument %d may not be const",
165  function_name.c_str(), param);
166  }
167  return NULL;
168  }
169  }
170 
171  if (report_errors) {
172  return Dtool_Raise_ArgTypeError(self, param, function_name.c_str(), classdef->_PyType.tp_name);
173  }
174 
175  return NULL;
176 }
177 
178 void *DTOOL_Call_GetPointerThis(PyObject *self) {
179  if (self != NULL) {
180  if (DtoolCanThisBeAPandaInstance(self)) {
181  Dtool_PyInstDef * pyself = (Dtool_PyInstDef *) self;
182  return pyself->_ptr_to_object;
183  }
184  }
185  return NULL;
186 }
187 
188 #ifndef NDEBUG
189 ////////////////////////////////////////////////////////////////////
190 // Function: Dtool_CheckErrorOccurred
191 // Description: This is similar to a PyErr_Occurred() check, except
192 // that it also checks Notify to see if an assertion
193 // has occurred. If that is the case, then it raises
194 // an AssertionError.
195 //
196 // Returns true if there is an active exception, false
197 // otherwise.
198 //
199 // In the NDEBUG case, this is simply a #define to
200 // _PyErr_OCCURRED() (which is an undocumented inline
201 // version of PyErr_Occurred()).
202 ////////////////////////////////////////////////////////////////////
203 bool Dtool_CheckErrorOccurred() {
204  if (_PyErr_OCCURRED()) {
205  return true;
206  }
207  if (Notify::ptr()->has_assert_failed()) {
208  Dtool_Raise_AssertionError();
209  return true;
210  }
211  return false;
212 }
213 #endif // NDEBUG
214 
215 ////////////////////////////////////////////////////////////////////
216 // Function: Dtool_Raise_AssertionError
217 // Description: Raises an AssertionError containing the last thrown
218 // assert message, and clears the assertion flag.
219 // Returns NULL.
220 ////////////////////////////////////////////////////////////////////
221 PyObject *Dtool_Raise_AssertionError() {
222  Notify *notify = Notify::ptr();
223 #if PY_MAJOR_VERSION >= 3
224  PyObject *message = PyUnicode_FromString(notify->get_assert_error_message().c_str());
225 #else
226  PyObject *message = PyString_FromString(notify->get_assert_error_message().c_str());
227 #endif
228  Py_INCREF(PyExc_AssertionError);
229  PyErr_Restore(PyExc_AssertionError, message, (PyObject *)NULL);
230  notify->clear_assert_failed();
231  return NULL;
232 }
233 
234 ////////////////////////////////////////////////////////////////////
235 // Function: Dtool_Raise_TypeError
236 // Description: Raises a TypeError with the given message, and
237 // returns NULL.
238 ////////////////////////////////////////////////////////////////////
239 PyObject *Dtool_Raise_TypeError(const char *message) {
240  // PyErr_Restore is what PyErr_SetString would have ended up calling
241  // eventually anyway, so we might as well just get to the point.
242  Py_INCREF(PyExc_TypeError);
243 #if PY_MAJOR_VERSION >= 3
244  PyErr_Restore(PyExc_TypeError, PyUnicode_FromString(message), (PyObject *)NULL);
245 #else
246  PyErr_Restore(PyExc_TypeError, PyString_FromString(message), (PyObject *)NULL);
247 #endif
248  return NULL;
249 }
250 
251 ////////////////////////////////////////////////////////////////////
252 // Function: Dtool_Raise_ArgTypeError
253 // Description: Raises a TypeError of the form:
254 // function_name() argument n must be type, not type
255 // for a given object passed to a function.
256 //
257 // Always returns NULL so that it can be conveniently
258 // used as a return expression for wrapper functions
259 // that return a PyObject pointer.
260 ////////////////////////////////////////////////////////////////////
261 PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name) {
262 #if PY_MAJOR_VERSION >= 3
263  PyObject *message = PyUnicode_FromFormat(
264 #else
265  PyObject *message = PyString_FromFormat(
266 #endif
267  "%s() argument %d must be %s, not %s",
268  function_name, param, type_name,
269  Py_TYPE(obj)->tp_name);
270 
271  Py_INCREF(PyExc_TypeError);
272  PyErr_Restore(PyExc_TypeError, message, (PyObject *)NULL);
273  return NULL;
274 }
275 
276 ////////////////////////////////////////////////////////////////////
277 // Function: Dtool_Raise_AttributeError
278 // Description: Raises an AttributeError of the form:
279 // 'type' has no attribute 'attr'
280 //
281 // Always returns NULL so that it can be conveniently
282 // used as a return expression for wrapper functions
283 // that return a PyObject pointer.
284 ////////////////////////////////////////////////////////////////////
285 PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute) {
286 #if PY_MAJOR_VERSION >= 3
287  PyObject *message = PyUnicode_FromFormat(
288 #else
289  PyObject *message = PyString_FromFormat(
290 #endif
291  "'%.100s' object has no attribute '%.200s'",
292  Py_TYPE(obj)->tp_name, attribute);
293 
294  Py_INCREF(PyExc_TypeError);
295  PyErr_Restore(PyExc_TypeError, message, (PyObject *)NULL);
296  return NULL;
297 }
298 
299 ////////////////////////////////////////////////////////////////////
300 // Function: Dtool_Raise_BadArgumentsError
301 // Description: Raises a TypeError of the form:
302 // Arguments must match:
303 // <list of overloads>
304 //
305 // However, in release builds, this instead is defined
306 // to a function that just prints out a generic
307 // message, to help reduce the amount of strings in
308 // the compiled library.
309 //
310 // Always returns NULL so that it can be conveniently
311 // used as a return expression for wrapper functions
312 // that return a PyObject pointer.
313 ////////////////////////////////////////////////////////////////////
314 PyObject *_Dtool_Raise_BadArgumentsError() {
315  return Dtool_Raise_TypeError("arguments do not match any function overload");
316 }
317 
318 ////////////////////////////////////////////////////////////////////
319 // Function: Dtool_Return_None
320 // Description: Convenience method that checks for exceptions, and
321 // if one occurred, returns NULL, otherwise Py_None.
322 ////////////////////////////////////////////////////////////////////
323 PyObject *_Dtool_Return_None() {
324  if (_PyErr_OCCURRED()) {
325  return NULL;
326  }
327 #ifndef NDEBUG
328  if (Notify::ptr()->has_assert_failed()) {
329  return Dtool_Raise_AssertionError();
330  }
331 #endif
332  Py_INCREF(Py_None);
333  return Py_None;
334 }
335 
336 ////////////////////////////////////////////////////////////////////
337 // Function: Dtool_Return_Bool
338 // Description: Convenience method that checks for exceptions, and
339 // if one occurred, returns NULL, otherwise the given
340 // boolean value as a PyObject *.
341 ////////////////////////////////////////////////////////////////////
342 PyObject *Dtool_Return_Bool(bool value) {
343  if (_PyErr_OCCURRED()) {
344  return NULL;
345  }
346 #ifndef NDEBUG
347  if (Notify::ptr()->has_assert_failed()) {
348  return Dtool_Raise_AssertionError();
349  }
350 #endif
351  PyObject *result = (value ? Py_True : Py_False);
352  Py_INCREF(result);
353  return result;
354 }
355 
356 ////////////////////////////////////////////////////////////////////
357 // Function: Dtool_Return
358 // Description: Convenience method that checks for exceptions, and
359 // if one occurred, returns NULL, otherwise the given
360 // return value. Its reference count is not increased.
361 ////////////////////////////////////////////////////////////////////
362 PyObject *_Dtool_Return(PyObject *value) {
363  if (_PyErr_OCCURRED()) {
364  return NULL;
365  }
366 #ifndef NDEBUG
367  if (Notify::ptr()->has_assert_failed()) {
368  return Dtool_Raise_AssertionError();
369  }
370 #endif
371  return value;
372 }
373 
374 ////////////////////////////////////////////////////////////////////////
375 // Function : DTool_CreatePyInstanceTyped
376 //
377 // this function relies on the behavior of typed objects in the panda system.
378 //
379 ////////////////////////////////////////////////////////////////////////
380 PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int type_index) {
381  // We can't do the NULL check here like in DTool_CreatePyInstance, since
382  // the caller will have to get the type index to pass to this function
383  // to begin with. That code probably would have crashed by now if it was
384  // really NULL for whatever reason.
385  nassertr(local_this_in != NULL, NULL);
386 
387  /////////////////////////////////////////////////////
388  // IF the class is possibly a run time typed object
389  /////////////////////////////////////////////////////
390  if (type_index > 0) {
391  /////////////////////////////////////////////////////
392  // get best fit class...
393  /////////////////////////////////////////////////////
394  Dtool_PyTypedObject *target_class = Dtool_RuntimeTypeDtoolType(type_index);
395  if (target_class != NULL) {
396  /////////////////////////////////////////////////////
397  // cast to the type...
398  //////////////////////////////////////////////////////
399  void *new_local_this = target_class->_Dtool_DowncastInterface(local_this_in, &known_class_type);
400  if (new_local_this != NULL) {
401  /////////////////////////////////////////////
402  // ask class to allocate an instance..
403  /////////////////////////////////////////////
404  Dtool_PyInstDef *self = (Dtool_PyInstDef *) target_class->As_PyTypeObject().tp_new(&target_class->As_PyTypeObject(), NULL, NULL);
405  if (self != NULL) {
406  self->_ptr_to_object = new_local_this;
407  self->_memory_rules = memory_rules;
408  self->_is_const = is_const;
409  //self->_signature = PY_PANDA_SIGNATURE;
410  self->_My_Type = target_class;
411  return (PyObject *)self;
412  }
413  }
414  }
415  }
416 
417  /////////////////////////////////////////////////////
418  // if we get this far .. just wrap the thing in the known type ??
419  // better than aborting...I guess....
420  /////////////////////////////////////////////////////
421  Dtool_PyInstDef *self = (Dtool_PyInstDef *) known_class_type.As_PyTypeObject().tp_new(&known_class_type.As_PyTypeObject(), NULL, NULL);
422  if (self != NULL) {
423  self->_ptr_to_object = local_this_in;
424  self->_memory_rules = memory_rules;
425  self->_is_const = is_const;
426  //self->_signature = PY_PANDA_SIGNATURE;
427  self->_My_Type = &known_class_type;
428  }
429  return (PyObject *)self;
430 }
431 
432 ////////////////////////////////////////////////////////////////////////
433 // DTool_CreatePyInstance .. wrapper function to finalize the existance of a general
434 // dtool py instance..
435 ////////////////////////////////////////////////////////////////////////
436 PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const) {
437  if (local_this == NULL) {
438  // This is actually a very common case, so let's allow this, but return
439  // Py_None consistently. This eliminates code in the wrappers.
440  Py_INCREF(Py_None);
441  return Py_None;
442  }
443 
444  Dtool_PyTypedObject *classdef = &in_classdef;
445  Dtool_PyInstDef *self = (Dtool_PyInstDef *) classdef->As_PyTypeObject().tp_new(&classdef->As_PyTypeObject(), NULL, NULL);
446  if (self != NULL) {
447  self->_ptr_to_object = local_this;
448  self->_memory_rules = memory_rules;
449  self->_is_const = is_const;
450  self->_My_Type = classdef;
451  }
452  return (PyObject *)self;
453 }
454 
455 ///////////////////////////////////////////////////////////////////////////////
456 /// Th Finalizer for simple instances..
457 ///////////////////////////////////////////////////////////////////////////////
458 int DTool_PyInit_Finalize(PyObject *self, void *local_this, Dtool_PyTypedObject *type, bool memory_rules, bool is_const) {
459  // lets put some code in here that checks to see the memory is properly configured..
460  // prior to my call ..
461 
462  ((Dtool_PyInstDef *)self)->_My_Type = type;
463  ((Dtool_PyInstDef *)self)->_ptr_to_object = local_this;
464  ((Dtool_PyInstDef *)self)->_memory_rules = memory_rules;
465  ((Dtool_PyInstDef *)self)->_is_const = is_const;
466  return 0;
467 }
468 
469 ///////////////////////////////////////////////////////////////////////////////
470 // A helper function to glue method definition together .. that can not be done
471 // at code generation time because of multiple generation passes in interrogate..
472 //
473 ///////////////////////////////////////////////////////////////////////////////
474 void Dtool_Accum_MethDefs(PyMethodDef in[], MethodDefmap &themap) {
475  for (; in->ml_name != NULL; in++) {
476  if (themap.find(in->ml_name) == themap.end()) {
477  themap[in->ml_name] = in;
478  }
479  }
480 }
481 
482 ///////////////////////////////////////////////////////////////////////////////
483 // ** HACK ** alert..
484 //
485 // Need to keep a runtime type dictionary ... that is forward declared of typed object.
486 // We rely on the fact that typed objects are uniquly defined by an integer.
487 //
488 ///////////////////////////////////////////////////////////////////////////////
489 void
490 RegisterRuntimeClass(Dtool_PyTypedObject *otype, int class_id) {
491  if (class_id == 0) {
492  interrogatedb_cat.warning()
493  << "Class " << otype->_PyType.tp_name
494  << " has a zero TypeHandle value; check that init_type() is called.\n";
495 
496  } else if (class_id > 0) {
497  RunTimeTypeDictionary &dict = GetRunTimeDictionary();
498  pair<RunTimeTypeDictionary::iterator, bool> result =
499  dict.insert(RunTimeTypeDictionary::value_type(class_id, otype));
500  if (!result.second) {
501  // There was already an entry in the dictionary for class_id.
502  Dtool_PyTypedObject *other_type = (*result.first).second;
503  interrogatedb_cat.warning()
504  << "Classes " << otype->_PyType.tp_name
505  << " and " << other_type->_PyType.tp_name
506  << " share the same TypeHandle value (" << class_id
507  << "); check class definitions.\n";
508 
509  } else {
510  GetRunTimeTypeList().insert(class_id);
511  otype->_type = TypeRegistry::ptr()->find_type_by_id(class_id);
512  }
513  }
514 }
515 
516 ///////////////////////////////////////////////////////////////////////////////
517 ///////////////////////////////////////////////////////////////////////////////
518 Dtool_PyTypedObject *Dtool_RuntimeTypeDtoolType(int type) {
519  RunTimeTypeDictionary::iterator di = GetRunTimeDictionary().find(type);
520  if (di != GetRunTimeDictionary().end()) {
521  return di->second;
522  } else {
523  int type2 = get_best_parent_from_Set(type, GetRunTimeTypeList());
524  di = GetRunTimeDictionary().find(type2);
525  if (di != GetRunTimeDictionary().end()) {
526  return di->second;
527  }
528  }
529  return NULL;
530 }
531 
532 ///////////////////////////////////////////////////////////////////////////////
533 #if PY_MAJOR_VERSION >= 3
534 PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], PyModuleDef *module_def) {
535 #else
536 PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
537 #endif
538  // the module level function inits....
539  MethodDefmap functions;
540  for (int xx = 0; defs[xx] != NULL; xx++) {
541  Dtool_Accum_MethDefs(defs[xx]->_methods, functions);
542  }
543 
544  PyMethodDef *newdef = new PyMethodDef[functions.size() + 1];
545  MethodDefmap::iterator mi;
546  int offset = 0;
547  for (mi = functions.begin(); mi != functions.end(); mi++, offset++) {
548  newdef[offset] = *mi->second;
549  }
550  newdef[offset].ml_doc = NULL;
551  newdef[offset].ml_name = NULL;
552  newdef[offset].ml_meth = NULL;
553  newdef[offset].ml_flags = 0;
554 
555 #if PY_MAJOR_VERSION >= 3
556  module_def->m_methods = newdef;
557  PyObject *module = PyModule_Create(module_def);
558 #else
559  PyObject *module = Py_InitModule((char *)modulename, newdef);
560 #endif
561 
562  if (module == NULL) {
563 #if PY_MAJOR_VERSION >= 3
564  return Dtool_Raise_TypeError("PyModule_Create returned NULL");
565 #else
566  return Dtool_Raise_TypeError("Py_InitModule returned NULL");
567 #endif
568  }
569 
570  // the constant inits... enums, classes ...
571  for (int y = 0; defs[y] != NULL; y++) {
572  defs[y]->_constants(module);
573  }
574 
575  PyModule_AddIntConstant(module, "Dtool_PyNativeInterface", 1);
576  return module;
577 }
578 
579 ///////////////////////////////////////////////////////////////////////////////
580 /// HACK.... Be careful
581 //
582 // Dtool_BorrowThisReference
583 // This function can be used to grab the "THIS" pointer from an object and use it
584 // Required to support historical inheritance in the form of "is this instance of"..
585 //
586 ///////////////////////////////////////////////////////////////////////////////
587 PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args) {
588  PyObject *from_in = NULL;
589  PyObject *to_in = NULL;
590  if (PyArg_UnpackTuple(args, "Dtool_BorrowThisReference", 2, 2, &to_in, &from_in)) {
591 
592  if (DtoolCanThisBeAPandaInstance(from_in) && DtoolCanThisBeAPandaInstance(to_in)) {
593  Dtool_PyInstDef *from = (Dtool_PyInstDef *) from_in;
594  Dtool_PyInstDef *to = (Dtool_PyInstDef *) to_in;
595 
596  //if (PyObject_TypeCheck(to_in, Py_TYPE(from_in))) {
597  if (from->_My_Type == to->_My_Type) {
598  to->_memory_rules = false;
599  to->_is_const = from->_is_const;
600  to->_ptr_to_object = from->_ptr_to_object;
601 
602  Py_INCREF(Py_None);
603  return Py_None;
604  }
605 
606  return PyErr_Format(PyExc_TypeError, "types %s and %s do not match",
607  Py_TYPE(from)->tp_name, Py_TYPE(to)->tp_name);
608  } else {
609  return Dtool_Raise_TypeError("One of these does not appear to be DTOOL Instance ??");
610  }
611  }
612  return (PyObject *) NULL;
613 }
614 
615 //////////////////////////////////////////////////////////////////////////////////////////////
616 // We do expose a dictionay for dtool classes .. this should be removed at some point..
617 //////////////////////////////////////////////////////////////////////////////////////////////
618 PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args) {
619  PyObject *self;
620  PyObject *subject;
621  PyObject *key;
622  if (PyArg_ParseTuple(args, "OSO", &self, &key, &subject)) {
623  PyObject *dict = ((PyTypeObject *)self)->tp_dict;
624  if (dict == NULL || !PyDict_Check(dict)) {
625  return Dtool_Raise_TypeError("No dictionary On Object");
626  } else {
627  PyDict_SetItem(dict, key, subject);
628  }
629  }
630  if (PyErr_Occurred()) {
631  return (PyObject *)NULL;
632  }
633  Py_INCREF(Py_None);
634  return Py_None;
635 }
636 
637 ///////////////////////////////////////////////////////////////////////////////////
638 Py_hash_t DTOOL_PyObject_HashPointer(PyObject *self) {
639  if (self != NULL && DtoolCanThisBeAPandaInstance(self)) {
640  Dtool_PyInstDef * pyself = (Dtool_PyInstDef *) self;
641  return (Py_hash_t) pyself->_ptr_to_object;
642  }
643  return -1;
644 }
645 
646 /* Compare v to w. Return
647  -1 if v < w or exception (PyErr_Occurred() true in latter case).
648  0 if v == w.
649  1 if v > w.
650  XXX The docs (C API manual) say the return value is undefined in case
651  XXX of error.
652 */
653 
654 int DTOOL_PyObject_ComparePointers(PyObject *v1, PyObject *v2) {
655  // try this compare
656  void *v1_this = DTOOL_Call_GetPointerThis(v1);
657  void *v2_this = DTOOL_Call_GetPointerThis(v2);
658  if (v1_this != NULL && v2_this != NULL) { // both are our types...
659  if (v1_this < v2_this) {
660  return -1;
661  }
662  if (v1_this > v2_this) {
663  return 1;
664  }
665  return 0;
666  }
667 
668  // ok self compare...
669  if (v1 < v2) {
670  return -1;
671  }
672  if (v1 > v2) {
673  return 1;
674  }
675  return 0;
676 }
677 
678 int DTOOL_PyObject_Compare(PyObject *v1, PyObject *v2) {
679  // First try compareTo function..
680  PyObject * func = PyObject_GetAttrString(v1, "compare_to");
681  if (func == NULL) {
682  PyErr_Clear();
683  } else {
684  PyObject *res = NULL;
685  PyObject *args = PyTuple_Pack(1, v2);
686  if (args != NULL) {
687  res = PyObject_Call(func, args, NULL);
688  Py_DECREF(args);
689  }
690  Py_DECREF(func);
691  PyErr_Clear(); // just in case the function threw an error
692  // only use if the function returns an INT... hmm
693  if (res != NULL) {
694  if (PyLong_Check(res)) {
695  long answer = PyLong_AsLong(res);
696  Py_DECREF(res);
697 
698  // Python really wants us to return strictly -1, 0, or 1.
699  if (answer < 0) {
700  return -1;
701  } else if (answer > 0) {
702  return 1;
703  } else {
704  return 0;
705  }
706  }
707 #if PY_MAJOR_VERSION < 3
708  else if (PyInt_Check(res)) {
709  long answer = PyInt_AsLong(res);
710  Py_DECREF(res);
711 
712  // Python really wants us to return strictly -1, 0, or 1.
713  if (answer < 0) {
714  return -1;
715  } else if (answer > 0) {
716  return 1;
717  } else {
718  return 0;
719  }
720  }
721 #endif
722  Py_DECREF(res);
723  }
724  }
725 
726  return DTOOL_PyObject_ComparePointers(v1, v2);
727 }
728 
729 PyObject *DTOOL_PyObject_RichCompare(PyObject *v1, PyObject *v2, int op) {
730  int cmpval = DTOOL_PyObject_Compare(v1, v2);
731  bool result;
732  switch (op) {
733  case Py_LT:
734  result = (cmpval < 0);
735  break;
736  case Py_LE:
737  result = (cmpval <= 0);
738  break;
739  case Py_EQ:
740  result = (cmpval == 0);
741  break;
742  case Py_NE:
743  result = (cmpval != 0);
744  break;
745  case Py_GT:
746  result = (cmpval > 0);
747  break;
748  case Py_GE:
749  result = (cmpval >= 0);
750  }
751  return PyBool_FromLong(result);
752 }
753 
754 PyObject *make_list_for_item(PyObject *self, const char *num_name,
755  const char *element_name) {
756  PyObject *num_result = PyObject_CallMethod(self, (char *)num_name, (char *)"()");
757  if (num_result == NULL) {
758  return NULL;
759  }
760 
761  Py_ssize_t num_elements;
762 #if PY_MAJOR_VERSION >= 3
763  num_elements = PyLong_AsSsize_t(num_result);
764 #else
765  num_elements = PyInt_AsSsize_t(num_result);
766 #endif
767  Py_DECREF(num_result);
768 
769  PyObject *list = PyList_New(num_elements);
770  for (int i = 0; i < num_elements; ++i) {
771  PyObject *element = PyObject_CallMethod(self, (char *)element_name, (char *)"(i)", i);
772  if (element == NULL) {
773  Py_DECREF(list);
774  return NULL;
775  }
776  PyList_SET_ITEM(list, i, element);
777  }
778  return list;
779 }
780 
781 ////////////////////////////////////////////////////////////////////
782 // Function: copy_from_make_copy
783 // Description: This is a support function for a synthesized
784 // __copy__() method from a C++ make_copy() method.
785 ////////////////////////////////////////////////////////////////////
786 PyObject *copy_from_make_copy(PyObject *self) {
787  return PyObject_CallMethod(self, (char *)"make_copy", (char *)"()");
788 }
789 
790 ////////////////////////////////////////////////////////////////////
791 // Function: copy_from_copy_constructor
792 // Description: This is a support function for a synthesized
793 // __copy__() method from a C++ copy constructor.
794 ////////////////////////////////////////////////////////////////////
795 PyObject *copy_from_copy_constructor(PyObject *self) {
796  PyObject *this_class = PyObject_Type(self);
797  if (this_class == NULL) {
798  return NULL;
799  }
800 
801  PyObject *result = PyObject_CallFunction(this_class, (char *)"(O)", self);
802  Py_DECREF(this_class);
803  return result;
804 }
805 
806 ////////////////////////////////////////////////////////////////////
807 // Function: map_deepcopy_to_copy
808 // Description: This is a support function for a synthesized
809 // __deepcopy__() method for any class that has a
810 // __copy__() method. The sythethic method simply
811 // invokes __copy__().
812 ////////////////////////////////////////////////////////////////////
813 PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) {
814  return PyObject_CallMethod(self, (char *)"__copy__", (char *)"()");
815 }
816 
817 ////////////////////////////////////////////////////////////////////
818 // Function: PyLongOrInt_FromUnsignedLong
819 // Description: Similar to PyLong_FromUnsignedLong(), but returns
820 // either a regular integer or a long integer, according
821 // to whether the indicated value will fit.
822 ////////////////////////////////////////////////////////////////////
823 #if PY_MAJOR_VERSION < 3
824 EXPCL_DTOOLCONFIG PyObject *
825 PyLongOrInt_FromUnsignedLong(unsigned long value) {
826  if ((long)value < 0) {
827  return PyLong_FromUnsignedLong(value);
828  } else {
829  return PyInt_FromLong((long)value);
830  }
831 }
832 #endif
833 
834 #endif // HAVE_PYTHON
TypeHandle find_type_by_id(int id) const
Looks for a previously-registered type with the given id number (as returned by TypeHandle::get_index...
void clear_assert_failed()
Resets the assert_failed flag that is set whenever an assertion test fails.
Definition: pnotify.I:59
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:337
static TypeRegistry * ptr()
Returns the pointer to the global TypeRegistry object.
const string & get_assert_error_message() const
Returns the error message that corresponds to the assertion that most recently failed.
Definition: pnotify.I:48
An object that handles general error reporting to the user.
Definition: pnotify.h:38