Panda3D
Loading...
Searching...
No Matches
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"
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
16using std::string;
17
18/**
19
20 */
21void 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 */
34bool 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 */
52bool 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 */
94void *
95DTOOL_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 */
140bool _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 */
155PyObject *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 */
171PyObject *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 */
190PyObject *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 */
211PyObject *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 */
235PyObject *_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 */
243PyObject *_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 */
260PyObject *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 */
279PyObject *_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 */
295static 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
329static 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
340static 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 */
358PyTypeObject *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 */
458PyObject *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..
502PyObject *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 */
524Dtool_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
540PyObject *Dtool_PyModuleInitHelper(const LibraryDef *defs[], PyModuleDef *module_def) {
541#else
542PyObject *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.
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"..
698PyObject *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..
728EXPCL_PYPANDA PyObject *
729Dtool_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 */
752PyObject *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_CallNoArgs(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 */
766PyObject *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 */
776PyObject *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_CallNoArgs(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 */
790bool 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 && PY_VERSION_HEX < 0x030D0000
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 */
820bool 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 */
836bool 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 */
871bool 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:44
std::string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
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