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