Panda3D
py_panda.cxx
Go to the documentation of this file.
1 /**
2  * @file py_panda.cxx
3  * @author drose
4  * @date 2005-07-04
5  */
6 
7 #include "py_panda.h"
8 #include "config_interrogatedb.h"
9 #include "executionEnvironment.h"
10 
11 #ifdef HAVE_PYTHON
12 
13 using std::string;
14 
15 /**
16 
17  */
18 void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer) {
19  if (DtoolInstance_Check(self)) {
20  *answer = DtoolInstance_UPCAST(self, *classdef);
21  } else {
22  *answer = nullptr;
23  }
24 }
25 
26 /**
27  * This is a support function for the Python bindings: it extracts the
28  * underlying C++ pointer of the given type for a given Python object. If it
29  * was of the wrong type, raises an AttributeError.
30  */
31 bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer) {
32  if (self == nullptr || !DtoolInstance_Check(self) || DtoolInstance_VOID_PTR(self) == nullptr) {
33  Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
34  return false;
35  }
36 
37  *answer = DtoolInstance_UPCAST(self, classdef);
38  return true;
39 }
40 
41 /**
42  * The same thing as Dtool_Call_ExtractThisPointer, except that it performs
43  * the additional check that the pointer is a non-const pointer. This is
44  * called by function wrappers for functions of which all overloads are non-
45  * const, and saves a bit of code.
46  *
47  * The extra method_name argument is used in formatting the error message.
48  */
49 bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
50  void **answer, const char *method_name) {
51 
52  if (self == nullptr || !DtoolInstance_Check(self) || DtoolInstance_VOID_PTR(self) == nullptr) {
53  Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
54  return false;
55  }
56 
57  if (DtoolInstance_IS_CONST(self)) {
58  // All overloads of this function are non-const.
59  PyErr_Format(PyExc_TypeError,
60  "Cannot call %s() on a const object.",
61  method_name);
62  return false;
63  }
64 
65  *answer = DtoolInstance_UPCAST(self, classdef);
66  return true;
67 }
68 
69 /**
70  * Extracts the C++ pointer for an object, given its Python wrapper object,
71  * for passing as the parameter to a C++ function.
72  *
73  * self is the Python wrapper object in question.
74  *
75  * classdef is the Python class wrapper for the C++ class in which the this
76  * pointer should be returned. (This may require an upcast operation, if self
77  * is not already an instance of classdef.)
78  *
79  * param and function_name are used for error reporting only, and describe the
80  * particular function and parameter index for this parameter.
81  *
82  * const_ok is true if the function is declared const and can therefore be
83  * called with either a const or non-const "this" pointer, or false if the
84  * function is declared non-const, and can therefore be called with only a
85  * non-const "this" pointer.
86  *
87  * The return value is the C++ pointer that was extracted, or NULL if there
88  * was a problem (in which case the Python exception state will have been
89  * set).
90  */
91 void *
92 DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef,
93  int param, const string &function_name, bool const_ok,
94  bool report_errors) {
95  // if (PyErr_Occurred()) { return nullptr; }
96  if (self == nullptr) {
97  if (report_errors) {
98  return Dtool_Raise_TypeError("self is nullptr");
99  }
100  return nullptr;
101  }
102 
103  if (DtoolInstance_Check(self)) {
104  void *result = DtoolInstance_UPCAST(self, *classdef);
105 
106  if (result != nullptr) {
107  if (const_ok || !DtoolInstance_IS_CONST(self)) {
108  return result;
109  }
110 
111  if (report_errors) {
112  return PyErr_Format(PyExc_TypeError,
113  "%s() argument %d may not be const",
114  function_name.c_str(), param);
115  }
116  return nullptr;
117  }
118  }
119 
120  if (report_errors) {
121  return Dtool_Raise_ArgTypeError(self, param, function_name.c_str(), classdef->_PyType.tp_name);
122  }
123 
124  return nullptr;
125 }
126 
127 /**
128  * This is similar to a PyErr_Occurred() check, except that it also checks
129  * Notify to see if an assertion has occurred. If that is the case, then it
130  * raises an AssertionError.
131  *
132  * Returns true if there is an active exception, false otherwise.
133  *
134  * In the NDEBUG case, this is simply a #define to _PyErr_OCCURRED() (which is
135  * an undocumented inline version of PyErr_Occurred()).
136  */
137 bool _Dtool_CheckErrorOccurred() {
138  if (_PyErr_OCCURRED()) {
139  return true;
140  }
141  if (Notify::ptr()->has_assert_failed()) {
142  Dtool_Raise_AssertionError();
143  return true;
144  }
145  return false;
146 }
147 
148 /**
149  * Raises an AssertionError containing the last thrown assert message, and
150  * clears the assertion flag. Returns NULL.
151  */
152 PyObject *Dtool_Raise_AssertionError() {
153  Notify *notify = Notify::ptr();
154 #if PY_MAJOR_VERSION >= 3
155  PyObject *message = PyUnicode_FromString(notify->get_assert_error_message().c_str());
156 #else
157  PyObject *message = PyString_FromString(notify->get_assert_error_message().c_str());
158 #endif
159  Py_INCREF(PyExc_AssertionError);
160  PyErr_Restore(PyExc_AssertionError, message, nullptr);
161  notify->clear_assert_failed();
162  return nullptr;
163 }
164 
165 /**
166  * Raises a TypeError with the given message, and returns NULL.
167  */
168 PyObject *Dtool_Raise_TypeError(const char *message) {
169  // PyErr_Restore is what PyErr_SetString would have ended up calling
170  // eventually anyway, so we might as well just get to the point.
171  Py_INCREF(PyExc_TypeError);
172 #if PY_MAJOR_VERSION >= 3
173  PyErr_Restore(PyExc_TypeError, PyUnicode_FromString(message), nullptr);
174 #else
175  PyErr_Restore(PyExc_TypeError, PyString_FromString(message), nullptr);
176 #endif
177  return nullptr;
178 }
179 
180 /**
181  * Raises a TypeError of the form: function_name() argument n must be type,
182  * not type for a given object passed to a function.
183  *
184  * Always returns NULL so that it can be conveniently used as a return
185  * expression for wrapper functions that return a PyObject pointer.
186  */
187 PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name) {
188 #if PY_MAJOR_VERSION >= 3
189  PyObject *message = PyUnicode_FromFormat(
190 #else
191  PyObject *message = PyString_FromFormat(
192 #endif
193  "%s() argument %d must be %s, not %s",
194  function_name, param, type_name,
195  Py_TYPE(obj)->tp_name);
196 
197  Py_INCREF(PyExc_TypeError);
198  PyErr_Restore(PyExc_TypeError, message, nullptr);
199  return nullptr;
200 }
201 
202 /**
203  * Raises an AttributeError of the form: 'type' has no attribute 'attr'
204  *
205  * Always returns NULL so that it can be conveniently used as a return
206  * expression for wrapper functions that return a PyObject pointer.
207  */
208 PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute) {
209 #if PY_MAJOR_VERSION >= 3
210  PyObject *message = PyUnicode_FromFormat(
211 #else
212  PyObject *message = PyString_FromFormat(
213 #endif
214  "'%.100s' object has no attribute '%.200s'",
215  Py_TYPE(obj)->tp_name, attribute);
216 
217  Py_INCREF(PyExc_AttributeError);
218  PyErr_Restore(PyExc_AttributeError, message, nullptr);
219  return nullptr;
220 }
221 
222 /**
223  * Raises a TypeError of the form: Arguments must match: <list of overloads>
224  *
225  * However, in release builds, this instead is defined to a function that just
226  * prints out a generic message, to help reduce the amount of strings in the
227  * compiled library.
228  *
229  * Always returns NULL so that it can be conveniently used as a return
230  * expression for wrapper functions that return a PyObject pointer.
231  */
232 PyObject *_Dtool_Raise_BadArgumentsError() {
233  return Dtool_Raise_TypeError("arguments do not match any function overload");
234 }
235 
236 /**
237  * Convenience method that checks for exceptions, and if one occurred, returns
238  * NULL, otherwise Py_None.
239  */
240 PyObject *_Dtool_Return_None() {
241  if (UNLIKELY(_PyErr_OCCURRED())) {
242  return nullptr;
243  }
244 #ifndef NDEBUG
245  if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
246  return Dtool_Raise_AssertionError();
247  }
248 #endif
249  Py_INCREF(Py_None);
250  return Py_None;
251 }
252 
253 /**
254  * Convenience method that checks for exceptions, and if one occurred, returns
255  * NULL, otherwise the given boolean value as a PyObject *.
256  */
257 PyObject *Dtool_Return_Bool(bool value) {
258  if (UNLIKELY(_PyErr_OCCURRED())) {
259  return nullptr;
260  }
261 #ifndef NDEBUG
262  if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
263  return Dtool_Raise_AssertionError();
264  }
265 #endif
266  PyObject *result = (value ? Py_True : Py_False);
267  Py_INCREF(result);
268  return result;
269 }
270 
271 /**
272  * Convenience method that checks for exceptions, and if one occurred, returns
273  * NULL, otherwise the given return value. Its reference count is not
274  * increased.
275  */
276 PyObject *_Dtool_Return(PyObject *value) {
277  if (UNLIKELY(_PyErr_OCCURRED())) {
278  return nullptr;
279  }
280 #ifndef NDEBUG
281  if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
282  return Dtool_Raise_AssertionError();
283  }
284 #endif
285  return value;
286 }
287 
288 #if PY_VERSION_HEX < 0x03040000
289 /**
290  * This function converts an int value to the appropriate enum instance.
291  */
292 static PyObject *Dtool_EnumType_New(PyTypeObject *subtype, PyObject *args, PyObject *kwds) {
293  PyObject *arg;
294  if (!Dtool_ExtractArg(&arg, args, kwds, "value")) {
295  return PyErr_Format(PyExc_TypeError,
296  "%s() missing 1 required argument: 'value'",
297  subtype->tp_name);
298  }
299 
300  if (Py_TYPE(arg) == subtype) {
301  Py_INCREF(arg);
302  return arg;
303  }
304 
305  PyObject *value2member = PyDict_GetItemString(subtype->tp_dict, "_value2member_map_");
306  nassertr_always(value2member != nullptr, nullptr);
307 
308  PyObject *member = PyDict_GetItem(value2member, arg);
309  if (member != nullptr) {
310  Py_INCREF(member);
311  return member;
312  }
313 
314  PyObject *repr = PyObject_Repr(arg);
315  PyErr_Format(PyExc_ValueError, "%s is not a valid %s",
316 #if PY_MAJOR_VERSION >= 3
317  PyUnicode_AS_STRING(repr),
318 #else
319  PyString_AS_STRING(repr),
320 #endif
321  subtype->tp_name);
322  Py_DECREF(repr);
323  return nullptr;
324 }
325 
326 static PyObject *Dtool_EnumType_Str(PyObject *self) {
327  PyObject *name = PyObject_GetAttrString(self, "name");
328 #if PY_MAJOR_VERSION >= 3
329  PyObject *repr = PyUnicode_FromFormat("%s.%s", Py_TYPE(self)->tp_name, PyString_AS_STRING(name));
330 #else
331  PyObject *repr = PyString_FromFormat("%s.%s", Py_TYPE(self)->tp_name, PyString_AS_STRING(name));
332 #endif
333  Py_DECREF(name);
334  return repr;
335 }
336 
337 static PyObject *Dtool_EnumType_Repr(PyObject *self) {
338  PyObject *name = PyObject_GetAttrString(self, "name");
339  PyObject *value = PyObject_GetAttrString(self, "value");
340 #if PY_MAJOR_VERSION >= 3
341  PyObject *repr = PyUnicode_FromFormat("<%s.%s: %ld>", Py_TYPE(self)->tp_name, PyString_AS_STRING(name), PyLongOrInt_AS_LONG(value));
342 #else
343  PyObject *repr = PyString_FromFormat("<%s.%s: %ld>", Py_TYPE(self)->tp_name, PyString_AS_STRING(name), PyLongOrInt_AS_LONG(value));
344 #endif
345  Py_DECREF(name);
346  Py_DECREF(value);
347  return repr;
348 }
349 #endif
350 
351 /**
352  * Creates a Python 3.4-style enum type. Steals reference to 'names', which
353  * should be a tuple of (name, value) pairs.
354  */
355 PyTypeObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *module) {
356  static PyObject *enum_class = nullptr;
357 #if PY_VERSION_HEX >= 0x03040000
358  static PyObject *enum_meta = nullptr;
359  static PyObject *enum_create = nullptr;
360  if (enum_meta == nullptr) {
361  PyObject *enum_module = PyImport_ImportModule("enum");
362  nassertr_always(enum_module != nullptr, nullptr);
363 
364  enum_class = PyObject_GetAttrString(enum_module, "Enum");
365  enum_meta = PyObject_GetAttrString(enum_module, "EnumMeta");
366  enum_create = PyObject_GetAttrString(enum_meta, "_create_");
367  nassertr(enum_meta != nullptr, nullptr);
368  }
369 
370  PyObject *result = PyObject_CallFunction(enum_create, (char *)"OsN", enum_class, name, names);
371  nassertr(result != nullptr, nullptr);
372 #else
373  static PyObject *name_str;
374  static PyObject *name_sunder_str;
375  static PyObject *value_str;
376  static PyObject *value_sunder_str;
377  static PyObject *value2member_map_sunder_str;
378  // Emulate something vaguely like the enum module.
379  if (enum_class == nullptr) {
380 #if PY_MAJOR_VERSION >= 3
381  name_str = PyUnicode_InternFromString("name");
382  value_str = PyUnicode_InternFromString("value");
383  name_sunder_str = PyUnicode_InternFromString("_name_");
384  value_sunder_str = PyUnicode_InternFromString("_value_");
385  value2member_map_sunder_str = PyUnicode_InternFromString("_value2member_map_");
386 #else
387  name_str = PyString_InternFromString("name");
388  value_str = PyString_InternFromString("value");
389  name_sunder_str = PyString_InternFromString("_name_");
390  value_sunder_str = PyString_InternFromString("_value_");
391  value2member_map_sunder_str = PyString_InternFromString("_value2member_map_");
392 #endif
393  PyObject *name_value_tuple = PyTuple_New(4);
394  PyTuple_SET_ITEM(name_value_tuple, 0, name_str);
395  PyTuple_SET_ITEM(name_value_tuple, 1, value_str);
396  PyTuple_SET_ITEM(name_value_tuple, 2, name_sunder_str);
397  PyTuple_SET_ITEM(name_value_tuple, 3, value_sunder_str);
398  Py_INCREF(name_str);
399  Py_INCREF(value_str);
400 
401  PyObject *slots_dict = PyDict_New();
402  PyDict_SetItemString(slots_dict, "__slots__", name_value_tuple);
403  Py_DECREF(name_value_tuple);
404 
405  enum_class = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s()N", "Enum", slots_dict);
406  nassertr(enum_class != nullptr, nullptr);
407  }
408 
409  // Create a subclass of this generic Enum class we just created.
410  PyObject *value2member = PyDict_New();
411  PyObject *dict = PyDict_New();
412  PyDict_SetItem(dict, value2member_map_sunder_str, value2member);
413  PyObject *result = PyObject_CallFunction((PyObject *)&PyType_Type, (char *)"s(O)N", name, enum_class, dict);
414  nassertr(result != nullptr, nullptr);
415 
416  ((PyTypeObject *)result)->tp_new = Dtool_EnumType_New;
417  ((PyTypeObject *)result)->tp_str = Dtool_EnumType_Str;
418  ((PyTypeObject *)result)->tp_repr = Dtool_EnumType_Repr;
419 
420  PyObject *empty_tuple = PyTuple_New(0);
421 
422  // Copy the names as instances of the above to the class dict, and create a
423  // reverse mapping in the _value2member_map_ dict.
424  Py_ssize_t size = PyTuple_GET_SIZE(names);
425  for (Py_ssize_t i = 0; i < size; ++i) {
426  PyObject *item = PyTuple_GET_ITEM(names, i);
427  PyObject *name = PyTuple_GET_ITEM(item, 0);
428  PyObject *value = PyTuple_GET_ITEM(item, 1);
429  PyObject *member = PyType_GenericNew((PyTypeObject *)result, empty_tuple, nullptr);
430  PyObject_SetAttr(member, name_str, name);
431  PyObject_SetAttr(member, name_sunder_str, name);
432  PyObject_SetAttr(member, value_str, value);
433  PyObject_SetAttr(member, value_sunder_str, value);
434  PyObject_SetAttr(result, name, member);
435  PyDict_SetItem(value2member, value, member);
436  Py_DECREF(member);
437  }
438  Py_DECREF(names);
439  Py_DECREF(value2member);
440  Py_DECREF(empty_tuple);
441 #endif
442 
443  if (module != nullptr) {
444  PyObject *modstr = PyUnicode_FromString(module);
445  PyObject_SetAttrString(result, "__module__", modstr);
446  Py_DECREF(modstr);
447  }
448  nassertr(PyType_Check(result), nullptr);
449  return (PyTypeObject *)result;
450 }
451 
452 /**
453 
454  */
455 PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int type_index) {
456  // We can't do the NULL check here like in DTool_CreatePyInstance, since the
457  // caller will have to get the type index to pass to this function to begin
458  // with. That code probably would have crashed by now if it was really NULL
459  // for whatever reason.
460  nassertr(local_this_in != nullptr, nullptr);
461 
462  // IF the class is possibly a run time typed object
463  if (type_index > 0) {
464  // get best fit class...
465  Dtool_PyTypedObject *target_class = (Dtool_PyTypedObject *)TypeHandle::from_index(type_index).get_python_type();
466  if (target_class != nullptr) {
467  // cast to the type...
468  void *new_local_this = target_class->_Dtool_DowncastInterface(local_this_in, &known_class_type);
469  if (new_local_this != nullptr) {
470  // ask class to allocate an instance..
471  Dtool_PyInstDef *self = (Dtool_PyInstDef *) target_class->_PyType.tp_new(&target_class->_PyType, nullptr, nullptr);
472  if (self != nullptr) {
473  self->_ptr_to_object = new_local_this;
474  self->_memory_rules = memory_rules;
475  self->_is_const = is_const;
476  // self->_signature = PY_PANDA_SIGNATURE;
477  self->_My_Type = target_class;
478  return (PyObject *)self;
479  }
480  }
481  }
482  }
483 
484  // if we get this far .. just wrap the thing in the known type ?? better
485  // than aborting...I guess....
486  Dtool_PyInstDef *self = (Dtool_PyInstDef *) known_class_type._PyType.tp_new(&known_class_type._PyType, nullptr, nullptr);
487  if (self != nullptr) {
488  self->_ptr_to_object = local_this_in;
489  self->_memory_rules = memory_rules;
490  self->_is_const = is_const;
491  // self->_signature = PY_PANDA_SIGNATURE;
492  self->_My_Type = &known_class_type;
493  }
494  return (PyObject *)self;
495 }
496 
497 // DTool_CreatePyInstance .. wrapper function to finalize the existance of a
498 // general dtool py instance..
499 PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const) {
500  if (local_this == nullptr) {
501  // This is actually a very common case, so let's allow this, but return
502  // Py_None consistently. This eliminates code in the wrappers.
503  Py_INCREF(Py_None);
504  return Py_None;
505  }
506 
507  Dtool_PyTypedObject *classdef = &in_classdef;
508  Dtool_PyInstDef *self = (Dtool_PyInstDef *) classdef->_PyType.tp_new(&classdef->_PyType, nullptr, nullptr);
509  if (self != nullptr) {
510  self->_ptr_to_object = local_this;
511  self->_memory_rules = memory_rules;
512  self->_is_const = is_const;
513  self->_My_Type = classdef;
514  }
515  return (PyObject *)self;
516 }
517 
518 /**
519  * Returns a borrowed reference to the global type dictionary.
520  */
521 Dtool_TypeMap *Dtool_GetGlobalTypeMap() {
522  PyObject *capsule = PySys_GetObject((char *)"_interrogate_types");
523  if (capsule != nullptr) {
524  return (Dtool_TypeMap *)PyCapsule_GetPointer(capsule, nullptr);
525  } else {
526  Dtool_TypeMap *type_map = new Dtool_TypeMap;
527  capsule = PyCapsule_New((void *)type_map, nullptr, nullptr);
528  PySys_SetObject((char *)"_interrogate_types", capsule);
529  Py_DECREF(capsule);
530  return type_map;
531  }
532 }
533 
534 #if PY_MAJOR_VERSION >= 3
535 PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def) {
536 #else
537 PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], const char *modulename) {
538 #endif
539  // Check the version so we can print a helpful error if it doesn't match.
540  string version = Py_GetVersion();
541 
542  if (version[0] != '0' + PY_MAJOR_VERSION ||
543  version[2] != '0' + PY_MINOR_VERSION) {
544  // Raise a helpful error message. We can safely do this because the
545  // signature and behavior for PyErr_SetString has remained consistent.
546  std::ostringstream errs;
547  errs << "this module was compiled for Python "
548  << PY_MAJOR_VERSION << "." << PY_MINOR_VERSION << ", which is "
549  << "incompatible with Python " << version.substr(0, 3);
550  string error = errs.str();
551  PyErr_SetString(PyExc_ImportError, error.c_str());
552  return nullptr;
553  }
554 
555  Dtool_TypeMap *type_map = Dtool_GetGlobalTypeMap();
556 
557  // the module level function inits....
558  MethodDefmap functions;
559  for (size_t i = 0; defs[i] != nullptr; i++) {
560  const LibraryDef &def = *defs[i];
561 
562  // Accumulate method definitions.
563  for (PyMethodDef *meth = def._methods; meth->ml_name != nullptr; meth++) {
564  if (functions.find(meth->ml_name) == functions.end()) {
565  functions[meth->ml_name] = meth;
566  }
567  }
568 
569  // Define exported types.
570  const Dtool_TypeDef *types = def._types;
571  if (types != nullptr) {
572  while (types->name != nullptr) {
573  (*type_map)[std::string(types->name)] = types->type;
574  ++types;
575  }
576  }
577  }
578 
579  // Resolve external types, in a second pass.
580  for (size_t i = 0; defs[i] != nullptr; i++) {
581  const LibraryDef &def = *defs[i];
582 
583  Dtool_TypeDef *types = def._external_types;
584  if (types != nullptr) {
585  while (types->name != nullptr) {
586  auto it = type_map->find(std::string(types->name));
587  if (it != type_map->end()) {
588  types->type = it->second;
589  } else {
590  return PyErr_Format(PyExc_NameError, "name '%s' is not defined", types->name);
591  }
592  ++types;
593  }
594  }
595  }
596 
597  PyMethodDef *newdef = new PyMethodDef[functions.size() + 1];
598  MethodDefmap::iterator mi;
599  int offset = 0;
600  for (mi = functions.begin(); mi != functions.end(); mi++, offset++) {
601  newdef[offset] = *mi->second;
602  }
603  newdef[offset].ml_doc = nullptr;
604  newdef[offset].ml_name = nullptr;
605  newdef[offset].ml_meth = nullptr;
606  newdef[offset].ml_flags = 0;
607 
608 #if PY_MAJOR_VERSION >= 3
609  module_def->m_methods = newdef;
610  PyObject *module = PyModule_Create(module_def);
611 #else
612  PyObject *module = Py_InitModule((char *)modulename, newdef);
613 #endif
614 
615  if (module == nullptr) {
616 #if PY_MAJOR_VERSION >= 3
617  return Dtool_Raise_TypeError("PyModule_Create returned NULL");
618 #else
619  return Dtool_Raise_TypeError("Py_InitModule returned NULL");
620 #endif
621  }
622 
623  // MAIN_DIR needs to be set very early; this seems like a convenient place
624  // to do that. Perhaps we'll find a better place for this in the future.
625  static bool initialized_main_dir = false;
626  if (!initialized_main_dir) {
627  if (interrogatedb_cat.is_debug()) {
628  // Good opportunity to print this out once, at startup.
629  interrogatedb_cat.debug()
630  << "Python " << version << "\n";
631  }
632 
634  // Grab the __main__ module.
635  PyObject *main_module = PyImport_ImportModule("__main__");
636  if (main_module == NULL) {
637  interrogatedb_cat.warning() << "Unable to import __main__\n";
638  }
639 
640  // Extract the __file__ attribute, if present.
641  Filename main_dir;
642  PyObject *file_attr = nullptr;
643  if (main_module != nullptr) {
644  file_attr = PyObject_GetAttrString(main_module, "__file__");
645  }
646  if (file_attr == nullptr) {
647  // Must be running in the interactive interpreter. Use the CWD.
648  main_dir = ExecutionEnvironment::get_cwd();
649  } else {
650 #if PY_MAJOR_VERSION >= 3
651  Py_ssize_t length;
652  wchar_t *buffer = PyUnicode_AsWideCharString(file_attr, &length);
653  if (buffer != nullptr) {
654  main_dir = Filename::from_os_specific_w(std::wstring(buffer, length));
655  main_dir.make_absolute();
656  main_dir = main_dir.get_dirname();
657  PyMem_Free(buffer);
658  }
659 #else
660  char *buffer;
661  Py_ssize_t length;
662  if (PyString_AsStringAndSize(file_attr, &buffer, &length) != -1) {
663  main_dir = Filename::from_os_specific(std::string(buffer, length));
664  main_dir.make_absolute();
665  main_dir = main_dir.get_dirname();
666  }
667 #endif
668  else {
669  interrogatedb_cat.warning() << "Invalid string for __main__.__file__\n";
670  }
671  }
673  PyErr_Clear();
674  }
675  initialized_main_dir = true;
676 
677  // Also, while we are at it, initialize the thread swap hook.
678 #if defined(HAVE_THREADS) && defined(SIMPLE_THREADS)
679  global_thread_state_swap = PyThreadState_Swap;
680 #endif
681  }
682 
683  PyModule_AddIntConstant(module, "Dtool_PyNativeInterface", 1);
684  return module;
685 }
686 
687 // HACK.... Be careful Dtool_BorrowThisReference This function can be used to
688 // grab the "THIS" pointer from an object and use it Required to support
689 // historical inheritance in the form of "is this instance of"..
690 PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args) {
691  PyObject *from_in = nullptr;
692  PyObject *to_in = nullptr;
693  if (PyArg_UnpackTuple(args, "Dtool_BorrowThisReference", 2, 2, &to_in, &from_in)) {
694 
695  if (DtoolInstance_Check(from_in) && DtoolInstance_Check(to_in)) {
696  Dtool_PyInstDef *from = (Dtool_PyInstDef *) from_in;
697  Dtool_PyInstDef *to = (Dtool_PyInstDef *) to_in;
698 
699  // if (PyObject_TypeCheck(to_in, Py_TYPE(from_in))) {
700  if (from->_My_Type == to->_My_Type) {
701  to->_memory_rules = false;
702  to->_is_const = from->_is_const;
703  to->_ptr_to_object = from->_ptr_to_object;
704 
705  Py_INCREF(Py_None);
706  return Py_None;
707  }
708 
709  return PyErr_Format(PyExc_TypeError, "types %s and %s do not match",
710  Py_TYPE(from)->tp_name, Py_TYPE(to)->tp_name);
711  } else {
712  return Dtool_Raise_TypeError("One of these does not appear to be DTOOL Instance ??");
713  }
714  }
715  return nullptr;
716 }
717 
718 // We do expose a dictionay for dtool classes .. this should be removed at
719 // some point..
720 EXPCL_PYPANDA PyObject *
721 Dtool_AddToDictionary(PyObject *self1, PyObject *args) {
722  PyObject *self;
723  PyObject *subject;
724  PyObject *key;
725  if (PyArg_ParseTuple(args, "OSO", &self, &key, &subject)) {
726  PyObject *dict = ((PyTypeObject *)self)->tp_dict;
727  if (dict == nullptr || !PyDict_Check(dict)) {
728  return Dtool_Raise_TypeError("No dictionary On Object");
729  } else {
730  PyDict_SetItem(dict, key, subject);
731  }
732  }
733  if (PyErr_Occurred()) {
734  return nullptr;
735  }
736  Py_INCREF(Py_None);
737  return Py_None;
738 }
739 
740 /**
741  * This is a support function for a synthesized __copy__() method from a C++
742  * make_copy() method.
743  */
744 PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) {
745  PyObject *callable = PyObject_GetAttrString(self, "make_copy");
746  if (callable == nullptr) {
747  return nullptr;
748  }
749  PyObject *result = _PyObject_CallNoArg(callable);
750  Py_DECREF(callable);
751  return result;
752 }
753 
754 /**
755  * This is a support function for a synthesized __copy__() method from a C++
756  * copy constructor.
757  */
758 PyObject *copy_from_copy_constructor(PyObject *self, PyObject *noargs) {
759  PyObject *callable = (PyObject *)Py_TYPE(self);
760  return _PyObject_FastCall(callable, &self, 1);
761 }
762 
763 /**
764  * This is a support function for a synthesized __deepcopy__() method for any
765  * class that has a __copy__() method. The sythethic method simply invokes
766  * __copy__().
767  */
768 PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) {
769  PyObject *callable = PyObject_GetAttrString(self, "__copy__");
770  if (callable == nullptr) {
771  return nullptr;
772  }
773  PyObject *result = _PyObject_CallNoArg(callable);
774  Py_DECREF(callable);
775  return result;
776 }
777 
778 /**
779  * A more efficient version of PyArg_ParseTupleAndKeywords for the special
780  * case where there is only a single PyObject argument.
781  */
782 bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds,
783  const char *keyword) {
784 
785  if (PyTuple_GET_SIZE(args) == 1) {
786  if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) {
787  *result = PyTuple_GET_ITEM(args, 0);
788  return true;
789  }
790  } else if (PyTuple_GET_SIZE(args) == 0) {
791  PyObject *key;
792  Py_ssize_t ppos = 0;
793  if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1 &&
794  PyDict_Next(kwds, &ppos, &key, result)) {
795  // We got the item, we just need to make sure that it had the right key.
796 #if PY_VERSION_HEX >= 0x03060000
797  return PyUnicode_CheckExact(key) && _PyUnicode_EqualToASCIIString(key, keyword);
798 #elif PY_MAJOR_VERSION >= 3
799  return PyUnicode_CheckExact(key) && PyUnicode_CompareWithASCIIString(key, keyword) == 0;
800 #else
801  return PyString_CheckExact(key) && strcmp(PyString_AS_STRING(key), keyword) == 0;
802 #endif
803  }
804  }
805 
806  return false;
807 }
808 
809 /**
810  * Variant of Dtool_ExtractArg that does not accept a keyword argument.
811  */
812 bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds) {
813  if (PyTuple_GET_SIZE(args) == 1 &&
814  (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0)) {
815  *result = PyTuple_GET_ITEM(args, 0);
816  return true;
817  }
818  return false;
819 }
820 
821 /**
822  * A more efficient version of PyArg_ParseTupleAndKeywords for the special
823  * case where there is only a single optional PyObject argument.
824  *
825  * Returns true if valid (including if there were 0 items), false if there was
826  * an error, such as an invalid number of parameters.
827  */
828 bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds,
829  const char *keyword) {
830 
831  if (PyTuple_GET_SIZE(args) == 1) {
832  if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) {
833  *result = PyTuple_GET_ITEM(args, 0);
834  return true;
835  }
836  } else if (PyTuple_GET_SIZE(args) == 0) {
837  if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1) {
838  PyObject *key;
839  Py_ssize_t ppos = 0;
840  if (!PyDict_Next(kwds, &ppos, &key, result)) {
841  return true;
842  }
843 
844  // We got the item, we just need to make sure that it had the right key.
845 #if PY_VERSION_HEX >= 0x03060000
846  return PyUnicode_CheckExact(key) && _PyUnicode_EqualToASCIIString(key, keyword);
847 #elif PY_MAJOR_VERSION >= 3
848  return PyUnicode_CheckExact(key) && PyUnicode_CompareWithASCIIString(key, keyword) == 0;
849 #else
850  return PyString_CheckExact(key) && strcmp(PyString_AS_STRING(key), keyword) == 0;
851 #endif
852  } else {
853  return true;
854  }
855  }
856 
857  return false;
858 }
859 
860 /**
861  * Variant of Dtool_ExtractOptionalArg that does not accept a keyword argument.
862  */
863 bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds) {
864  if (kwds != nullptr && PyDict_GET_SIZE(kwds) != 0) {
865  return false;
866  }
867  if (PyTuple_GET_SIZE(args) == 1) {
868  *result = PyTuple_GET_ITEM(args, 0);
869  return true;
870  }
871  return (PyTuple_GET_SIZE(args) == 0);
872 }
873 
874 #endif // HAVE_PYTHON
std::string get_dirname() const
Returns the directory part of the filename.
Definition: filename.I:358
get_cwd
Returns the name of the current working directory.
const std::string & get_assert_error_message() const
Returns the error message that corresponds to the assertion that most recently failed.
Definition: pnotify.I:38
void clear_assert_failed()
Resets the assert_failed flag that is set whenever an assertion test fails.
Definition: pnotify.I:47
static Notify * ptr()
Returns the pointer to the global Notify object.
Definition: notify.cxx:289
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static void shadow_environment_variable(const std::string &var, const std::string &value)
Changes the apparent definition of the indicated environment variable by masking it within this class...
PyObject * DTool_CreatePyInstance(const T *obj, bool memory_rules)
These functions wrap a pointer for a class that defines get_type_handle().
Definition: py_panda.I:111
void make_absolute()
Converts the filename to a fully-qualified pathname from the root (if it is a relative pathname),...
Definition: filename.cxx:968
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1123
static Filename from_os_specific_w(const std::wstring &os_specific, Type type=T_general)
The wide-string variant of from_os_specific().
Definition: filename.cxx:394
has_environment_variable
Returns true if the indicated environment variable is defined.
An object that handles general error reporting to the user.
Definition: pnotify.h:33
static Filename from_os_specific(const std::string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes,...
Definition: filename.cxx:328