open_toontown_panda3d/dtool/src/interrogatedb/py_panda.cxx

861 lines
28 KiB
C++

/**
* PANDA 3D SOFTWARE
* Copyright (c) Carnegie Mellon University. All rights reserved.
*
* All use of this software is subject to the terms of the revised BSD
* license. You should have received a copy of this license along
* with this source code in a file named "LICENSE."
*
* @file py_panda.cxx
* @author drose
* @date 2005-07-04
*/
#include "py_panda.h"
#include "config_interrogatedb.h"
#include "executionEnvironment.h"
#ifdef HAVE_PYTHON
PyMemberDef standard_type_members[] = {
{(char *)"this", (sizeof(void*) == sizeof(int)) ? T_UINT : T_ULONGLONG, offsetof(Dtool_PyInstDef, _ptr_to_object), READONLY, (char *)"C++ 'this' pointer, if any"},
{(char *)"this_ownership", T_BOOL, offsetof(Dtool_PyInstDef, _memory_rules), READONLY, (char *)"C++ 'this' ownership rules"},
{(char *)"this_const", T_BOOL, offsetof(Dtool_PyInstDef, _is_const), READONLY, (char *)"C++ 'this' const flag"},
// {(char *)"this_signature", T_INT, offsetof(Dtool_PyInstDef, _signature),
// READONLY, (char *)"A type check signature"},
{(char *)"this_metatype", T_OBJECT, offsetof(Dtool_PyInstDef, _My_Type), READONLY, (char *)"The dtool meta object"},
{nullptr} /* Sentinel */
};
static RuntimeTypeMap runtime_type_map;
static RuntimeTypeSet runtime_type_set;
static NamedTypeMap named_type_map;
/**
*/
void DTOOL_Call_ExtractThisPointerForType(PyObject *self, Dtool_PyTypedObject *classdef, void **answer) {
if (DtoolInstance_Check(self)) {
*answer = DtoolInstance_UPCAST(self, *classdef);
} else {
*answer = nullptr;
}
}
/**
* This is a support function for the Python bindings: it extracts the
* underlying C++ pointer of the given type for a given Python object. If it
* was of the wrong type, raises an AttributeError.
*/
bool Dtool_Call_ExtractThisPointer(PyObject *self, Dtool_PyTypedObject &classdef, void **answer) {
if (self == nullptr || !DtoolInstance_Check(self) || DtoolInstance_VOID_PTR(self) == nullptr) {
Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
return false;
}
*answer = DtoolInstance_UPCAST(self, classdef);
return true;
}
/**
* The same thing as Dtool_Call_ExtractThisPointer, except that it performs
* the additional check that the pointer is a non-const pointer. This is
* called by function wrappers for functions of which all overloads are non-
* const, and saves a bit of code.
*
* The extra method_name argument is used in formatting the error message.
*/
bool Dtool_Call_ExtractThisPointer_NonConst(PyObject *self, Dtool_PyTypedObject &classdef,
void **answer, const char *method_name) {
if (self == nullptr || !DtoolInstance_Check(self) || DtoolInstance_VOID_PTR(self) == nullptr) {
Dtool_Raise_TypeError("C++ object is not yet constructed, or already destructed.");
return false;
}
if (DtoolInstance_IS_CONST(self)) {
// All overloads of this function are non-const.
PyErr_Format(PyExc_TypeError,
"Cannot call %s() on a const object.",
method_name);
return false;
}
*answer = DtoolInstance_UPCAST(self, classdef);
return true;
}
/**
* Extracts the C++ pointer for an object, given its Python wrapper object,
* for passing as the parameter to a C++ function.
*
* self is the Python wrapper object in question.
*
* classdef is the Python class wrapper for the C++ class in which the this
* pointer should be returned. (This may require an upcast operation, if self
* is not already an instance of classdef.)
*
* param and function_name are used for error reporting only, and describe the
* particular function and parameter index for this parameter.
*
* const_ok is true if the function is declared const and can therefore be
* called with either a const or non-const "this" pointer, or false if the
* function is declared non-const, and can therefore be called with only a
* non-const "this" pointer.
*
* The return value is the C++ pointer that was extracted, or NULL if there
* was a problem (in which case the Python exception state will have been
* set).
*/
void *
DTOOL_Call_GetPointerThisClass(PyObject *self, Dtool_PyTypedObject *classdef,
int param, const string &function_name, bool const_ok,
bool report_errors) {
// if (PyErr_Occurred()) { return nullptr; }
if (self == nullptr) {
if (report_errors) {
return Dtool_Raise_TypeError("self is nullptr");
}
return nullptr;
}
if (DtoolInstance_Check(self)) {
void *result = DtoolInstance_UPCAST(self, *classdef);
if (result != nullptr) {
if (const_ok || !DtoolInstance_IS_CONST(self)) {
return result;
}
if (report_errors) {
return PyErr_Format(PyExc_TypeError,
"%s() argument %d may not be const",
function_name.c_str(), param);
}
return nullptr;
}
}
if (report_errors) {
return Dtool_Raise_ArgTypeError(self, param, function_name.c_str(), classdef->_PyType.tp_name);
}
return nullptr;
}
/**
* This is similar to a PyErr_Occurred() check, except that it also checks
* Notify to see if an assertion has occurred. If that is the case, then it
* raises an AssertionError.
*
* Returns true if there is an active exception, false otherwise.
*
* In the NDEBUG case, this is simply a #define to _PyErr_OCCURRED() (which is
* an undocumented inline version of PyErr_Occurred()).
*/
bool _Dtool_CheckErrorOccurred() {
if (_PyErr_OCCURRED()) {
return true;
}
if (Notify::ptr()->has_assert_failed()) {
Dtool_Raise_AssertionError();
return true;
}
return false;
}
/**
* Raises an AssertionError containing the last thrown assert message, and
* clears the assertion flag. Returns NULL.
*/
PyObject *Dtool_Raise_AssertionError() {
Notify *notify = Notify::ptr();
#if PY_MAJOR_VERSION >= 3
PyObject *message = PyUnicode_FromString(notify->get_assert_error_message().c_str());
#else
PyObject *message = PyString_FromString(notify->get_assert_error_message().c_str());
#endif
Py_INCREF(PyExc_AssertionError);
PyErr_Restore(PyExc_AssertionError, message, nullptr);
notify->clear_assert_failed();
return nullptr;
}
/**
* Raises a TypeError with the given message, and returns NULL.
*/
PyObject *Dtool_Raise_TypeError(const char *message) {
// PyErr_Restore is what PyErr_SetString would have ended up calling
// eventually anyway, so we might as well just get to the point.
Py_INCREF(PyExc_TypeError);
#if PY_MAJOR_VERSION >= 3
PyErr_Restore(PyExc_TypeError, PyUnicode_FromString(message), nullptr);
#else
PyErr_Restore(PyExc_TypeError, PyString_FromString(message), nullptr);
#endif
return nullptr;
}
/**
* Raises a TypeError of the form: function_name() argument n must be type,
* not type for a given object passed to a function.
*
* Always returns NULL so that it can be conveniently used as a return
* expression for wrapper functions that return a PyObject pointer.
*/
PyObject *Dtool_Raise_ArgTypeError(PyObject *obj, int param, const char *function_name, const char *type_name) {
#if PY_MAJOR_VERSION >= 3
PyObject *message = PyUnicode_FromFormat(
#else
PyObject *message = PyString_FromFormat(
#endif
"%s() argument %d must be %s, not %s",
function_name, param, type_name,
Py_TYPE(obj)->tp_name);
Py_INCREF(PyExc_TypeError);
PyErr_Restore(PyExc_TypeError, message, nullptr);
return nullptr;
}
/**
* Raises an AttributeError of the form: 'type' has no attribute 'attr'
*
* Always returns NULL so that it can be conveniently used as a return
* expression for wrapper functions that return a PyObject pointer.
*/
PyObject *Dtool_Raise_AttributeError(PyObject *obj, const char *attribute) {
#if PY_MAJOR_VERSION >= 3
PyObject *message = PyUnicode_FromFormat(
#else
PyObject *message = PyString_FromFormat(
#endif
"'%.100s' object has no attribute '%.200s'",
Py_TYPE(obj)->tp_name, attribute);
Py_INCREF(PyExc_TypeError);
PyErr_Restore(PyExc_TypeError, message, nullptr);
return nullptr;
}
/**
* Raises a TypeError of the form: Arguments must match: <list of overloads>
*
* However, in release builds, this instead is defined to a function that just
* prints out a generic message, to help reduce the amount of strings in the
* compiled library.
*
* Always returns NULL so that it can be conveniently used as a return
* expression for wrapper functions that return a PyObject pointer.
*/
PyObject *_Dtool_Raise_BadArgumentsError() {
return Dtool_Raise_TypeError("arguments do not match any function overload");
}
/**
* Convenience method that checks for exceptions, and if one occurred, returns
* NULL, otherwise Py_None.
*/
PyObject *_Dtool_Return_None() {
if (UNLIKELY(_PyErr_OCCURRED())) {
return nullptr;
}
#ifndef NDEBUG
if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
return Dtool_Raise_AssertionError();
}
#endif
Py_INCREF(Py_None);
return Py_None;
}
/**
* Convenience method that checks for exceptions, and if one occurred, returns
* NULL, otherwise the given boolean value as a PyObject *.
*/
PyObject *Dtool_Return_Bool(bool value) {
if (UNLIKELY(_PyErr_OCCURRED())) {
return nullptr;
}
#ifndef NDEBUG
if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
return Dtool_Raise_AssertionError();
}
#endif
PyObject *result = (value ? Py_True : Py_False);
Py_INCREF(result);
return result;
}
/**
* Convenience method that checks for exceptions, and if one occurred, returns
* NULL, otherwise the given return value. Its reference count is not
* increased.
*/
PyObject *_Dtool_Return(PyObject *value) {
if (UNLIKELY(_PyErr_OCCURRED())) {
return nullptr;
}
#ifndef NDEBUG
if (UNLIKELY(Notify::ptr()->has_assert_failed())) {
return Dtool_Raise_AssertionError();
}
#endif
return value;
}
/**
* Creates a Python 3.4-style enum type. Steals reference to 'names'.
*/
PyObject *Dtool_EnumType_Create(const char *name, PyObject *names, const char *module) {
static PyObject *enum_class = nullptr;
static PyObject *enum_meta = nullptr;
static PyObject *enum_create = nullptr;
if (enum_meta == nullptr) {
PyObject *enum_module = PyImport_ImportModule("enum");
nassertr_always(enum_module != nullptr, nullptr);
enum_class = PyObject_GetAttrString(enum_module, "Enum");
enum_meta = PyObject_GetAttrString(enum_module, "EnumMeta");
enum_create = PyObject_GetAttrString(enum_meta, "_create_");
nassertr(enum_meta != nullptr, nullptr);
}
PyObject *result = PyObject_CallFunction(enum_create, (char *)"OsN", enum_class, name, names);
nassertr(result != nullptr, nullptr);
if (module != nullptr) {
PyObject *modstr = PyUnicode_FromString(module);
PyObject_SetAttrString(result, "__module__", modstr);
Py_DECREF(modstr);
}
return result;
}
/**
*/
PyObject *DTool_CreatePyInstanceTyped(void *local_this_in, Dtool_PyTypedObject &known_class_type, bool memory_rules, bool is_const, int type_index) {
// We can't do the NULL check here like in DTool_CreatePyInstance, since the
// caller will have to get the type index to pass to this function to begin
// with. That code probably would have crashed by now if it was really NULL
// for whatever reason.
nassertr(local_this_in != nullptr, nullptr);
// IF the class is possibly a run time typed object
if (type_index > 0) {
// get best fit class...
Dtool_PyTypedObject *target_class = Dtool_RuntimeTypeDtoolType(type_index);
if (target_class != nullptr) {
// cast to the type...
void *new_local_this = target_class->_Dtool_DowncastInterface(local_this_in, &known_class_type);
if (new_local_this != nullptr) {
// ask class to allocate an instance..
Dtool_PyInstDef *self = (Dtool_PyInstDef *) target_class->_PyType.tp_new(&target_class->_PyType, nullptr, nullptr);
if (self != nullptr) {
self->_ptr_to_object = new_local_this;
self->_memory_rules = memory_rules;
self->_is_const = is_const;
// self->_signature = PY_PANDA_SIGNATURE;
self->_My_Type = target_class;
return (PyObject *)self;
}
}
}
}
// if we get this far .. just wrap the thing in the known type ?? better
// than aborting...I guess....
Dtool_PyInstDef *self = (Dtool_PyInstDef *) known_class_type._PyType.tp_new(&known_class_type._PyType, nullptr, nullptr);
if (self != nullptr) {
self->_ptr_to_object = local_this_in;
self->_memory_rules = memory_rules;
self->_is_const = is_const;
// self->_signature = PY_PANDA_SIGNATURE;
self->_My_Type = &known_class_type;
}
return (PyObject *)self;
}
// DTool_CreatePyInstance .. wrapper function to finalize the existance of a
// general dtool py instance..
PyObject *DTool_CreatePyInstance(void *local_this, Dtool_PyTypedObject &in_classdef, bool memory_rules, bool is_const) {
if (local_this == nullptr) {
// This is actually a very common case, so let's allow this, but return
// Py_None consistently. This eliminates code in the wrappers.
Py_INCREF(Py_None);
return Py_None;
}
Dtool_PyTypedObject *classdef = &in_classdef;
Dtool_PyInstDef *self = (Dtool_PyInstDef *) classdef->_PyType.tp_new(&classdef->_PyType, nullptr, nullptr);
if (self != nullptr) {
self->_ptr_to_object = local_this;
self->_memory_rules = memory_rules;
self->_is_const = is_const;
self->_My_Type = classdef;
}
return (PyObject *)self;
}
// Th Finalizer for simple instances..
int DTool_PyInit_Finalize(PyObject *self, void *local_this, Dtool_PyTypedObject *type, bool memory_rules, bool is_const) {
// lets put some code in here that checks to see the memory is properly
// configured.. prior to my call ..
((Dtool_PyInstDef *)self)->_My_Type = type;
((Dtool_PyInstDef *)self)->_ptr_to_object = local_this;
((Dtool_PyInstDef *)self)->_memory_rules = memory_rules;
((Dtool_PyInstDef *)self)->_is_const = is_const;
return 0;
}
// A helper function to glue method definition together .. that can not be
// done at code generation time because of multiple generation passes in
// interrogate..
void Dtool_Accum_MethDefs(PyMethodDef in[], MethodDefmap &themap) {
for (; in->ml_name != nullptr; in++) {
if (themap.find(in->ml_name) == themap.end()) {
themap[in->ml_name] = in;
}
}
}
// ** HACK ** alert.. Need to keep a runtime type dictionary ... that is
// forward declared of typed object. We rely on the fact that typed objects
// are uniquly defined by an integer.
void
RegisterNamedClass(const string &name, Dtool_PyTypedObject &otype) {
pair<NamedTypeMap::iterator, bool> result =
named_type_map.insert(NamedTypeMap::value_type(name, &otype));
if (!result.second) {
// There was already a class with this name in the dictionary.
interrogatedb_cat.warning()
<< "Double definition for class " << name << "\n";
}
}
void
RegisterRuntimeTypedClass(Dtool_PyTypedObject &otype) {
int type_index = otype._type.get_index();
if (type_index == 0) {
interrogatedb_cat.warning()
<< "Class " << otype._PyType.tp_name
<< " has a zero TypeHandle value; check that init_type() is called.\n";
} else if (type_index < 0 || type_index >= TypeRegistry::ptr()->get_num_typehandles()) {
interrogatedb_cat.warning()
<< "Class " << otype._PyType.tp_name
<< " has an illegal TypeHandle value; check that init_type() is called.\n";
} else {
pair<RuntimeTypeMap::iterator, bool> result =
runtime_type_map.insert(RuntimeTypeMap::value_type(type_index, &otype));
if (!result.second) {
// There was already an entry in the dictionary for type_index.
Dtool_PyTypedObject *other_type = (*result.first).second;
interrogatedb_cat.warning()
<< "Classes " << otype._PyType.tp_name
<< " and " << other_type->_PyType.tp_name
<< " share the same TypeHandle value (" << type_index
<< "); check class definitions.\n";
} else {
runtime_type_set.insert(type_index);
}
}
}
Dtool_PyTypedObject *
LookupNamedClass(const string &name) {
NamedTypeMap::const_iterator it;
it = named_type_map.find(name);
if (it == named_type_map.end()) {
// Find a type named like this in the type registry.
TypeHandle handle = TypeRegistry::ptr()->find_type(name);
if (handle.get_index() > 0) {
RuntimeTypeMap::const_iterator it2;
it2 = runtime_type_map.find(handle.get_index());
if (it2 != runtime_type_map.end()) {
return it2->second;
}
}
interrogatedb_cat.error()
<< "Attempt to use type " << name << " which has not yet been defined!\n";
return nullptr;
} else {
return it->second;
}
}
Dtool_PyTypedObject *
LookupRuntimeTypedClass(TypeHandle handle) {
RuntimeTypeMap::const_iterator it;
it = runtime_type_map.find(handle.get_index());
if (it == runtime_type_map.end()) {
interrogatedb_cat.error()
<< "Attempt to use type " << handle << " which has not yet been defined!\n";
return nullptr;
} else {
return it->second;
}
}
Dtool_PyTypedObject *Dtool_RuntimeTypeDtoolType(int type) {
RuntimeTypeMap::iterator di = runtime_type_map.find(type);
if (di != runtime_type_map.end()) {
return di->second;
} else {
int type2 = get_best_parent_from_Set(type, runtime_type_set);
di = runtime_type_map.find(type2);
if (di != runtime_type_map.end()) {
return di->second;
}
}
return nullptr;
}
#if PY_MAJOR_VERSION >= 3
PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], PyModuleDef *module_def) {
#else
PyObject *Dtool_PyModuleInitHelper(LibraryDef *defs[], const char *modulename) {
#endif
// Check the version so we can print a helpful error if it doesn't match.
string version = Py_GetVersion();
if (version[0] != '0' + PY_MAJOR_VERSION ||
version[2] != '0' + PY_MINOR_VERSION) {
// Raise a helpful error message. We can safely do this because the
// signature and behavior for PyErr_SetString has remained consistent.
ostringstream errs;
errs << "this module was compiled for Python "
<< PY_MAJOR_VERSION << "." << PY_MINOR_VERSION << ", which is "
<< "incompatible with Python " << version.substr(0, 3);
string error = errs.str();
PyErr_SetString(PyExc_ImportError, error.c_str());
return nullptr;
}
// Initialize the types we define in py_panda.
static bool dtool_inited = false;
if (!dtool_inited) {
dtool_inited = true;
if (PyType_Ready(&Dtool_SequenceWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_SequenceWrapper)");
}
if (PyType_Ready(&Dtool_MutableSequenceWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_MutableSequenceWrapper)");
}
if (PyType_Ready(&Dtool_MappingWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper)");
}
if (PyType_Ready(&Dtool_MutableMappingWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_MutableMappingWrapper)");
}
if (PyType_Ready(&Dtool_MappingWrapper_Keys_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Keys)");
}
if (PyType_Ready(&Dtool_MappingWrapper_Values_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Values)");
}
if (PyType_Ready(&Dtool_MappingWrapper_Items_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_MappingWrapper_Items)");
}
if (PyType_Ready(&Dtool_GeneratorWrapper_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_GeneratorWrapper)");
}
if (PyType_Ready(&Dtool_StaticProperty_Type) < 0) {
return Dtool_Raise_TypeError("PyType_Ready(Dtool_StaticProperty_Type)");
}
// Initialize the base class of everything.
Dtool_PyModuleClassInit_DTOOL_SUPER_BASE(nullptr);
}
// the module level function inits....
MethodDefmap functions;
for (int xx = 0; defs[xx] != nullptr; xx++) {
Dtool_Accum_MethDefs(defs[xx]->_methods, functions);
}
PyMethodDef *newdef = new PyMethodDef[functions.size() + 1];
MethodDefmap::iterator mi;
int offset = 0;
for (mi = functions.begin(); mi != functions.end(); mi++, offset++) {
newdef[offset] = *mi->second;
}
newdef[offset].ml_doc = nullptr;
newdef[offset].ml_name = nullptr;
newdef[offset].ml_meth = nullptr;
newdef[offset].ml_flags = 0;
#if PY_MAJOR_VERSION >= 3
module_def->m_methods = newdef;
PyObject *module = PyModule_Create(module_def);
#else
PyObject *module = Py_InitModule((char *)modulename, newdef);
#endif
if (module == nullptr) {
#if PY_MAJOR_VERSION >= 3
return Dtool_Raise_TypeError("PyModule_Create returned NULL");
#else
return Dtool_Raise_TypeError("Py_InitModule returned NULL");
#endif
}
// MAIN_DIR needs to be set very early; this seems like a convenient place
// to do that. Perhaps we'll find a better place for this in the future.
static bool initialized_main_dir = false;
if (!initialized_main_dir) {
if (interrogatedb_cat.is_debug()) {
// Good opportunity to print this out once, at startup.
interrogatedb_cat.debug()
<< "Python " << version << "\n";
}
// Grab the __main__ module.
PyObject *main_module = PyImport_ImportModule("__main__");
if (main_module == nullptr) {
interrogatedb_cat.warning() << "Unable to import __main__\n";
}
// Extract the __file__ attribute, if present.
Filename main_dir;
PyObject *file_attr = PyObject_GetAttrString(main_module, "__file__");
if (file_attr == nullptr) {
// Must be running in the interactive interpreter. Use the CWD.
main_dir = ExecutionEnvironment::get_cwd();
} else {
#if PY_MAJOR_VERSION >= 3
Py_ssize_t length;
wchar_t *buffer = PyUnicode_AsWideCharString(file_attr, &length);
if (buffer != nullptr) {
main_dir = Filename::from_os_specific_w(std::wstring(buffer, length));
main_dir.make_absolute();
main_dir = main_dir.get_dirname();
PyMem_Free(buffer);
}
#else
char *buffer;
Py_ssize_t length;
if (PyString_AsStringAndSize(file_attr, &buffer, &length) != -1) {
main_dir = Filename::from_os_specific(std::string(buffer, length));
main_dir.make_absolute();
main_dir = main_dir.get_dirname();
}
#endif
else {
interrogatedb_cat.warning() << "Invalid string for __main__.__file__\n";
}
}
ExecutionEnvironment::shadow_environment_variable("MAIN_DIR", main_dir.to_os_specific());
PyErr_Clear();
initialized_main_dir = true;
}
PyModule_AddIntConstant(module, "Dtool_PyNativeInterface", 1);
return module;
}
// HACK.... Be careful Dtool_BorrowThisReference This function can be used to
// grab the "THIS" pointer from an object and use it Required to support
// historical inheritance in the form of "is this instance of"..
PyObject *Dtool_BorrowThisReference(PyObject *self, PyObject *args) {
PyObject *from_in = nullptr;
PyObject *to_in = nullptr;
if (PyArg_UnpackTuple(args, "Dtool_BorrowThisReference", 2, 2, &to_in, &from_in)) {
if (DtoolInstance_Check(from_in) && DtoolInstance_Check(to_in)) {
Dtool_PyInstDef *from = (Dtool_PyInstDef *) from_in;
Dtool_PyInstDef *to = (Dtool_PyInstDef *) to_in;
// if (PyObject_TypeCheck(to_in, Py_TYPE(from_in))) {
if (from->_My_Type == to->_My_Type) {
to->_memory_rules = false;
to->_is_const = from->_is_const;
to->_ptr_to_object = from->_ptr_to_object;
Py_INCREF(Py_None);
return Py_None;
}
return PyErr_Format(PyExc_TypeError, "types %s and %s do not match",
Py_TYPE(from)->tp_name, Py_TYPE(to)->tp_name);
} else {
return Dtool_Raise_TypeError("One of these does not appear to be DTOOL Instance ??");
}
}
return nullptr;
}
// We do expose a dictionay for dtool classes .. this should be removed at
// some point..
EXPCL_INTERROGATEDB PyObject *Dtool_AddToDictionary(PyObject *self1, PyObject *args) {
PyObject *self;
PyObject *subject;
PyObject *key;
if (PyArg_ParseTuple(args, "OSO", &self, &key, &subject)) {
PyObject *dict = ((PyTypeObject *)self)->tp_dict;
if (dict == nullptr || !PyDict_Check(dict)) {
return Dtool_Raise_TypeError("No dictionary On Object");
} else {
PyDict_SetItem(dict, key, subject);
}
}
if (PyErr_Occurred()) {
return nullptr;
}
Py_INCREF(Py_None);
return Py_None;
}
/**
* This is a support function for a synthesized __copy__() method from a C++
* make_copy() method.
*/
PyObject *copy_from_make_copy(PyObject *self, PyObject *noargs) {
PyObject *callable = PyObject_GetAttrString(self, "make_copy");
if (callable == nullptr) {
return nullptr;
}
PyObject *result = _PyObject_CallNoArg(callable);
Py_DECREF(callable);
return result;
}
/**
* This is a support function for a synthesized __copy__() method from a C++
* copy constructor.
*/
PyObject *copy_from_copy_constructor(PyObject *self, PyObject *noargs) {
PyObject *callable = (PyObject *)Py_TYPE(self);
return _PyObject_FastCall(callable, &self, 1);
}
/**
* This is a support function for a synthesized __deepcopy__() method for any
* class that has a __copy__() method. The sythethic method simply invokes
* __copy__().
*/
PyObject *map_deepcopy_to_copy(PyObject *self, PyObject *args) {
PyObject *callable = PyObject_GetAttrString(self, "__copy__");
if (callable == nullptr) {
return nullptr;
}
PyObject *result = _PyObject_CallNoArg(callable);
Py_DECREF(callable);
return result;
}
/**
* A more efficient version of PyArg_ParseTupleAndKeywords for the special
* case where there is only a single PyObject argument.
*/
bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds,
const char *keyword) {
if (PyTuple_GET_SIZE(args) == 1) {
if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) {
*result = PyTuple_GET_ITEM(args, 0);
return true;
}
} else if (PyTuple_GET_SIZE(args) == 0) {
PyObject *key;
Py_ssize_t ppos = 0;
if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1 &&
PyDict_Next(kwds, &ppos, &key, result)) {
// We got the item, we just need to make sure that it had the right key.
#if PY_VERSION_HEX >= 0x03060000
return PyUnicode_CheckExact(key) && _PyUnicode_EqualToASCIIString(key, keyword);
#elif PY_MAJOR_VERSION >= 3
return PyUnicode_CheckExact(key) && PyUnicode_CompareWithASCIIString(key, keyword) == 0;
#else
return PyString_CheckExact(key) && strcmp(PyString_AS_STRING(key), keyword) == 0;
#endif
}
}
return false;
}
/**
* Variant of Dtool_ExtractArg that does not accept a keyword argument.
*/
bool Dtool_ExtractArg(PyObject **result, PyObject *args, PyObject *kwds) {
if (PyTuple_GET_SIZE(args) == 1 &&
(kwds == nullptr || PyDict_GET_SIZE(kwds) == 0)) {
*result = PyTuple_GET_ITEM(args, 0);
return true;
}
return false;
}
/**
* A more efficient version of PyArg_ParseTupleAndKeywords for the special
* case where there is only a single optional PyObject argument.
*
* Returns true if valid (including if there were 0 items), false if there was
* an error, such as an invalid number of parameters.
*/
bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds,
const char *keyword) {
if (PyTuple_GET_SIZE(args) == 1) {
if (kwds == nullptr || PyDict_GET_SIZE(kwds) == 0) {
*result = PyTuple_GET_ITEM(args, 0);
return true;
}
} else if (PyTuple_GET_SIZE(args) == 0) {
if (kwds != nullptr && PyDict_GET_SIZE(kwds) == 1) {
PyObject *key;
Py_ssize_t ppos = 0;
if (!PyDict_Next(kwds, &ppos, &key, result)) {
return true;
}
// We got the item, we just need to make sure that it had the right key.
#if PY_VERSION_HEX >= 0x03060000
return PyUnicode_CheckExact(key) && _PyUnicode_EqualToASCIIString(key, keyword);
#elif PY_MAJOR_VERSION >= 3
return PyUnicode_CheckExact(key) && PyUnicode_CompareWithASCIIString(key, keyword) == 0;
#else
return PyString_CheckExact(key) && strcmp(PyString_AS_STRING(key), keyword) == 0;
#endif
} else {
return true;
}
}
return false;
}
/**
* Variant of Dtool_ExtractOptionalArg that does not accept a keyword argument.
*/
bool Dtool_ExtractOptionalArg(PyObject **result, PyObject *args, PyObject *kwds) {
if (kwds != nullptr && PyDict_GET_SIZE(kwds) != 0) {
return false;
}
if (PyTuple_GET_SIZE(args) == 1) {
*result = PyTuple_GET_ITEM(args, 0);
return true;
}
return (PyTuple_GET_SIZE(args) == 0);
}
#endif // HAVE_PYTHON