3 * ***** BEGIN GPL LICENSE BLOCK *****
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20 * All rights reserved.
22 * The Original Code is: all of this file.
24 * Contributor(s): none yet.
26 * ***** END GPL LICENSE BLOCK *****
29 /** \file gameengine/Expressions/PyObjectPlus.cpp
30 * \ingroup expressions
34 /*------------------------------
37 * C++ library routines for Crawl 3.2
39 * Derived from work by
42 * Computer Science Department
43 * Carnegie Mellon University (CMU)
44 * Center for the Neural Basis of Cognition (CNBC)
45 * http://www.python.org/doc/PyCPP.html
47 ------------------------------*/
48 #include <MT_assert.h>
50 #include "PyObjectPlus.h"
51 #include "STR_String.h"
52 #include "MT_Vector3.h"
53 #include "MEM_guardedalloc.h"
55 PyObjectPlus::~PyObjectPlus()
59 BGE_PROXY_REF(m_proxy)= NULL;
60 Py_DECREF(m_proxy); /* Remove own reference, python may still have 1 */
62 // assert(ob_refcnt==0);
66 PyObjectPlus::PyObjectPlus() : SG_QList() // constructor
73 void PyObjectPlus::ProcessReplica()
76 /* Clear the proxy, will be created again if needed with GetProxy()
77 * otherwise the PyObject will point to the wrong reference */
82 /* Sometimes we might want to manually invalidate a BGE type even if
83 * it hasnt been released by the BGE, say for example when an object
84 * is removed from a scene, accessing it may cause problems.
86 * In this case the current proxy is made invalid, disowned,
87 * and will raise an error on access. However if python can get access
88 * to this class again it will make a new proxy and work as expected.
90 void PyObjectPlus::InvalidateProxy() // check typename of each parent
94 BGE_PROXY_REF(m_proxy)=NULL;
104 /*------------------------------
105 * PyObjectPlus Type -- Every class, even the abstract one should have a Type
106 ------------------------------*/
109 PyTypeObject PyObjectPlus::Type = {
110 PyVarObject_HEAD_INIT(NULL, 0)
111 "PyObjectPlus", /*tp_name*/
112 sizeof(PyObjectPlus_Proxy), /*tp_basicsize*/
122 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
130 PyObject *PyObjectPlus::py_base_repr(PyObject *self) // This should be the entry in Type.
132 PyObjectPlus *self_plus= BGE_PROXY_REF(self);
133 if(self_plus==NULL) {
134 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
137 return self_plus->py_repr();
141 PyObject * PyObjectPlus::py_base_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
143 PyTypeObject *base_type;
144 PyObjectPlus_Proxy *base = NULL;
146 if (!PyArg_ParseTuple(args, "O:Base PyObjectPlus", &base))
149 /* the 'base' PyObject may be subclassed (multiple times even)
150 * we need to find the first C++ defined class to check 'type'
151 * is a subclass of the base arguments type.
153 * This way we can share one tp_new function for every PyObjectPlus
157 * # CustomOb is called 'type' in this C code
158 * class CustomOb(GameTypes.KX_GameObject):
161 * # this calls py_base_new(...), the type of 'CustomOb' is checked to be a subclass of the 'cont.owner' type
162 * ob = CustomOb(cont.owner)
165 base_type= Py_TYPE(base);
166 while(base_type && !BGE_PROXY_CHECK_TYPE(base_type))
167 base_type= base_type->tp_base;
169 if(base_type==NULL || !BGE_PROXY_CHECK_TYPE(base_type)) {
170 PyErr_SetString(PyExc_TypeError, "can't subclass from a blender game type because the argument given is not a game class or subclass");
174 /* use base_type rather then Py_TYPE(base) because we could already be subtyped */
175 if(!PyType_IsSubtype(type, base_type)) {
176 PyErr_Format(PyExc_TypeError, "can't subclass blender game type <%s> from <%s> because it is not a subclass", base_type->tp_name, type->tp_name);
180 /* invalidate the existing base and return a new subclassed one,
181 * this is a bit dodgy in that it also attaches its self to the existing object
182 * which is not really 'correct' python OO but for our use its OK. */
184 PyObjectPlus_Proxy *ret = (PyObjectPlus_Proxy *) type->tp_alloc(type, 0); /* starts with 1 ref, used for the return ref' */
187 ret->py_owns= base->py_owns;
188 ret->py_ref = base->py_ref;
191 base->ref= NULL; /* invalidate! disallow further access */
194 ret->ref->m_proxy= NULL;
195 /* 'base' may be free'd after this func finished but not necessarily
196 * there is no reference to the BGE data now so it will throw an error on access */
199 ret->ref->m_proxy= (PyObject *)ret; /* no need to add a ref because one is added when creating. */
200 Py_INCREF(ret); /* we return a new ref but m_proxy holds a ref so we need to add one */
203 // generic structures don't hold a reference to this proxy, so don't increment ref count
205 // but if the proxy owns the structure, there can be only one owner
209 return (PyObject *)ret;
212 void PyObjectPlus::py_base_dealloc(PyObject *self) // python wrapper
214 if (BGE_PROXY_PYREF(self)) {
215 PyObjectPlus *self_plus= BGE_PROXY_REF(self);
217 if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */
218 self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */
221 BGE_PROXY_REF(self)= NULL; // not really needed
223 // the generic pointer is not deleted directly, only through self_plus
224 BGE_PROXY_PTR(self)= NULL; // not really needed
226 void *ptr= BGE_PROXY_PTR(self);
228 if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it */
229 // generic structure owned by python MUST be created though MEM_alloc
232 BGE_PROXY_PTR(self)= NULL; // not really needed
236 /* is ok normally but not for subtyping, use tp_free instead. */
237 PyObject_DEL( self );
239 Py_TYPE(self)->tp_free(self);
243 /*------------------------------
244 * PyObjectPlus Methods -- Every class, even the abstract one should have a Methods
245 ------------------------------*/
246 PyMethodDef PyObjectPlus::Methods[] = {
247 {NULL, NULL} /* Sentinel */
250 #define attr_invalid (&(PyObjectPlus::Attributes[0]))
251 PyAttributeDef PyObjectPlus::Attributes[] = {
252 KX_PYATTRIBUTE_RO_FUNCTION("invalid", PyObjectPlus, pyattr_get_invalid),
258 PyObject* PyObjectPlus::pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
260 return PyBool_FromLong(self_v ? 0:1);
263 /* note, this is called as a python 'getset, where the PyAttributeDef is the closure */
264 PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *attrdef)
266 PyObjectPlus *ref= (BGE_PROXY_REF(self_py));
267 char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref;
268 if(ptr == NULL || (BGE_PROXY_PYREF(self_py) && (ref==NULL || !ref->py_is_valid()))) {
269 if(attrdef == attr_invalid)
270 Py_RETURN_TRUE; // dont bother running the function
272 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
276 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
278 // fake attribute, ignore
281 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
283 // the attribute has no field correspondance, handover processing to function.
284 if (attrdef->m_getFunction == NULL)
286 return (*attrdef->m_getFunction)(ptr, attrdef);
288 ptr += attrdef->m_offset;
289 if (attrdef->m_length > 1)
291 PyObject* resultlist = PyList_New(attrdef->m_length);
292 for (unsigned int i=0; i<attrdef->m_length; i++)
294 switch (attrdef->m_type) {
295 case KX_PYATTRIBUTE_TYPE_BOOL:
297 bool *val = reinterpret_cast<bool*>(ptr);
299 PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val));
302 case KX_PYATTRIBUTE_TYPE_SHORT:
304 short int *val = reinterpret_cast<short int*>(ptr);
305 ptr += sizeof(short int);
306 PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val));
309 case KX_PYATTRIBUTE_TYPE_ENUM:
310 // enum are like int, just make sure the field size is the same
311 if (sizeof(int) != attrdef->m_size)
313 Py_DECREF(resultlist);
317 case KX_PYATTRIBUTE_TYPE_INT:
319 int *val = reinterpret_cast<int*>(ptr);
321 PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val));
324 case KX_PYATTRIBUTE_TYPE_FLOAT:
326 float *val = reinterpret_cast<float*>(ptr);
327 ptr += sizeof(float);
328 PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(*val));
332 // no support for array of complex data
333 Py_DECREF(resultlist);
341 switch (attrdef->m_type) {
342 case KX_PYATTRIBUTE_TYPE_FLAG:
345 switch (attrdef->m_size) {
348 unsigned char *val = reinterpret_cast<unsigned char*>(ptr);
349 bval = (*val & attrdef->m_imin);
354 unsigned short *val = reinterpret_cast<unsigned short*>(ptr);
355 bval = (*val & attrdef->m_imin);
360 unsigned int *val = reinterpret_cast<unsigned int*>(ptr);
361 bval = (*val & attrdef->m_imin);
369 return PyLong_FromSsize_t(bval);
371 case KX_PYATTRIBUTE_TYPE_BOOL:
373 bool *val = reinterpret_cast<bool*>(ptr);
374 return PyLong_FromSsize_t(*val);
376 case KX_PYATTRIBUTE_TYPE_SHORT:
378 short int *val = reinterpret_cast<short int*>(ptr);
379 return PyLong_FromSsize_t(*val);
381 case KX_PYATTRIBUTE_TYPE_ENUM:
382 // enum are like int, just make sure the field size is the same
383 if (sizeof(int) != attrdef->m_size)
388 case KX_PYATTRIBUTE_TYPE_INT:
390 int *val = reinterpret_cast<int*>(ptr);
391 return PyLong_FromSsize_t(*val);
393 case KX_PYATTRIBUTE_TYPE_FLOAT:
395 float *val = reinterpret_cast<float*>(ptr);
396 if (attrdef->m_imin == 0) {
397 if (attrdef->m_imax == 0) {
398 return PyFloat_FromDouble(*val);
400 // vector, verify size
401 if (attrdef->m_size != attrdef->m_imax*sizeof(float))
406 return newVectorObject(val, attrdef->m_imax, Py_NEW, NULL);
408 PyObject* resultlist = PyList_New(attrdef->m_imax);
409 for (unsigned int i=0; i<attrdef->m_imax; i++)
411 PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(val[i]));
418 if (attrdef->m_size != attrdef->m_imax*attrdef->m_imin*sizeof(float))
423 return newMatrixObject(val, attrdef->m_imin, attrdef->m_imax, Py_WRAP, NULL);
425 PyObject* collist = PyList_New(attrdef->m_imin);
426 for (unsigned int i=0; i<attrdef->m_imin; i++)
428 PyObject* col = PyList_New(attrdef->m_imax);
429 for (unsigned int j=0; j<attrdef->m_imax; j++)
431 PyList_SET_ITEM(col,j,PyFloat_FromDouble(val[j]));
433 PyList_SET_ITEM(collist,i,col);
434 val += attrdef->m_imax;
440 case KX_PYATTRIBUTE_TYPE_VECTOR:
442 MT_Vector3 *val = reinterpret_cast<MT_Vector3*>(ptr);
444 float fval[3]= {(*val)[0], (*val)[1], (*val)[2]};
445 return newVectorObject(fval, 3, Py_NEW, NULL);
447 PyObject* resultlist = PyList_New(3);
448 for (unsigned int i=0; i<3; i++)
450 PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble((*val)[i]));
455 case KX_PYATTRIBUTE_TYPE_STRING:
457 STR_String *val = reinterpret_cast<STR_String*>(ptr);
458 return PyUnicode_FromString(*val);
460 case KX_PYATTRIBUTE_TYPE_CHAR:
462 return PyUnicode_FromString(ptr);
471 static bool py_check_attr_float(float *var, PyObject *value, const PyAttributeDef *attrdef)
473 double val = PyFloat_AsDouble(value);
474 if (val == -1.0 && PyErr_Occurred())
476 PyErr_Format(PyExc_TypeError, "expected float value for attribute \"%s\"", attrdef->m_name);
479 if (attrdef->m_clamp)
481 if (val < attrdef->m_fmin)
482 val = attrdef->m_fmin;
483 else if (val > attrdef->m_fmax)
484 val = attrdef->m_fmax;
486 else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
488 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
495 /* note, this is called as a python getset */
496 int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAttributeDef *attrdef)
498 PyObjectPlus *ref= (BGE_PROXY_REF(self_py));
499 char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref;
500 if(ref==NULL || !ref->py_is_valid() || ptr==NULL) {
501 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
502 return PY_SET_ATTR_FAIL;
505 void *undoBuffer = NULL;
506 void *sourceBuffer = NULL;
507 size_t bufferSize = 0;
508 PyObject *item = NULL; // to store object that must be dereferenced in case of error
509 PyObject *list = NULL; // to store object that must be dereferenced in case of error
511 ptr += attrdef->m_offset;
512 if (attrdef->m_length > 1)
514 if (!PySequence_Check(value))
516 PyErr_Format(PyExc_TypeError, "expected a sequence for attribute \"%s\"", attrdef->m_name);
517 return PY_SET_ATTR_FAIL;
519 if (PySequence_Size(value) != attrdef->m_length)
521 PyErr_Format(PyExc_TypeError, "incorrect number of elements in sequence for attribute \"%s\"", attrdef->m_name);
522 return PY_SET_ATTR_FAIL;
524 switch (attrdef->m_type)
526 case KX_PYATTRIBUTE_TYPE_FUNCTION:
527 if (attrdef->m_setFunction == NULL)
529 PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name);
530 return PY_SET_ATTR_FAIL;
532 return (*attrdef->m_setFunction)(ref, attrdef, value);
533 case KX_PYATTRIBUTE_TYPE_BOOL:
534 bufferSize = sizeof(bool);
536 case KX_PYATTRIBUTE_TYPE_SHORT:
537 bufferSize = sizeof(short int);
539 case KX_PYATTRIBUTE_TYPE_ENUM:
540 case KX_PYATTRIBUTE_TYPE_INT:
541 bufferSize = sizeof(int);
543 case KX_PYATTRIBUTE_TYPE_FLOAT:
544 bufferSize = sizeof(float);
548 PyErr_Format(PyExc_AttributeError, "Unsupported attribute type for attribute \"%s\", report to blender.org", attrdef->m_name);
549 return PY_SET_ATTR_FAIL;
551 // let's implement a smart undo method
552 bufferSize *= attrdef->m_length;
553 undoBuffer = malloc(bufferSize);
557 memcpy(undoBuffer, sourceBuffer, bufferSize);
559 for (int i=0; i<attrdef->m_length; i++)
561 item = PySequence_GetItem(value, i); /* new ref */
562 switch (attrdef->m_type)
564 case KX_PYATTRIBUTE_TYPE_BOOL:
566 bool *var = reinterpret_cast<bool*>(ptr);
568 if (PyLong_Check(item))
570 *var = (PyLong_AsSsize_t(item) != 0);
572 else if (PyBool_Check(item))
574 *var = (item == Py_True);
578 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
583 case KX_PYATTRIBUTE_TYPE_SHORT:
585 short int *var = reinterpret_cast<short int*>(ptr);
586 ptr += sizeof(short int);
587 if (PyLong_Check(item))
589 long val = PyLong_AsSsize_t(item);
590 if (attrdef->m_clamp)
592 if (val < attrdef->m_imin)
593 val = attrdef->m_imin;
594 else if (val > attrdef->m_imax)
595 val = attrdef->m_imax;
597 else if (val < attrdef->m_imin || val > attrdef->m_imax)
599 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
602 *var = (short int)val;
606 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
611 case KX_PYATTRIBUTE_TYPE_ENUM:
612 // enum are equivalent to int, just make sure that the field size matches:
613 if (sizeof(int) != attrdef->m_size)
615 PyErr_Format(PyExc_AttributeError, "Size check error for attribute, \"%s\", report to blender.org", attrdef->m_name);
619 case KX_PYATTRIBUTE_TYPE_INT:
621 int *var = reinterpret_cast<int*>(ptr);
623 if (PyLong_Check(item))
625 long val = PyLong_AsSsize_t(item);
626 if (attrdef->m_clamp)
628 if (val < attrdef->m_imin)
629 val = attrdef->m_imin;
630 else if (val > attrdef->m_imax)
631 val = attrdef->m_imax;
633 else if (val < attrdef->m_imin || val > attrdef->m_imax)
635 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
642 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
647 case KX_PYATTRIBUTE_TYPE_FLOAT:
649 float *var = reinterpret_cast<float*>(ptr);
650 ptr += sizeof(float);
651 double val = PyFloat_AsDouble(item);
652 if (val == -1.0 && PyErr_Occurred())
654 PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
657 else if (attrdef->m_clamp)
659 if (val < attrdef->m_fmin)
660 val = attrdef->m_fmin;
661 else if (val > attrdef->m_fmax)
662 val = attrdef->m_fmax;
664 else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
666 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
674 PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name);
677 // finished using item, release
681 // no error, call check function if any
682 if (attrdef->m_checkFunction != NULL)
684 if ((*attrdef->m_checkFunction)(ref, attrdef) != 0)
686 // if the checing function didnt set an error then set a generic one here so we dont set an error with no exception
687 if (PyErr_Occurred()==0)
688 PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", reasion unknown", attrdef->m_name);
690 // post check returned an error, restore values
694 memcpy(sourceBuffer, undoBuffer, bufferSize);
699 return PY_SET_ATTR_FAIL;
704 return PY_SET_ATTR_SUCCESS;
706 else // simple attribute value
708 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
710 if (attrdef->m_setFunction == NULL)
712 PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name);
713 return PY_SET_ATTR_FAIL;
715 return (*attrdef->m_setFunction)(ref, attrdef, value);
717 if (attrdef->m_checkFunction != NULL || attrdef->m_type == KX_PYATTRIBUTE_TYPE_VECTOR)
719 // post check function is provided, prepare undo buffer
721 switch (attrdef->m_type)
723 case KX_PYATTRIBUTE_TYPE_BOOL:
724 bufferSize = sizeof(bool);
726 case KX_PYATTRIBUTE_TYPE_SHORT:
727 bufferSize = sizeof(short);
729 case KX_PYATTRIBUTE_TYPE_ENUM:
730 case KX_PYATTRIBUTE_TYPE_FLAG:
731 case KX_PYATTRIBUTE_TYPE_CHAR:
732 bufferSize = attrdef->m_size;
734 case KX_PYATTRIBUTE_TYPE_INT:
735 bufferSize = sizeof(int);
737 case KX_PYATTRIBUTE_TYPE_FLOAT:
738 bufferSize = sizeof(float);
740 bufferSize *= attrdef->m_imax;
742 bufferSize *= attrdef->m_imin;
744 case KX_PYATTRIBUTE_TYPE_STRING:
745 sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr();
747 bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1;
749 case KX_PYATTRIBUTE_TYPE_VECTOR:
750 bufferSize = sizeof(MT_Vector3);
753 PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
754 return PY_SET_ATTR_FAIL;
758 undoBuffer = malloc(bufferSize);
761 memcpy(undoBuffer, sourceBuffer, bufferSize);
766 switch (attrdef->m_type)
768 case KX_PYATTRIBUTE_TYPE_BOOL:
770 bool *var = reinterpret_cast<bool*>(ptr);
771 if (PyLong_Check(value))
773 *var = (PyLong_AsSsize_t(value) != 0);
775 else if (PyBool_Check(value))
777 *var = (value == Py_True);
781 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
786 case KX_PYATTRIBUTE_TYPE_FLAG:
789 if (PyLong_Check(value))
791 bval = (PyLong_AsSsize_t(value) != 0);
793 else if (PyBool_Check(value))
795 bval = (value == Py_True);
799 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
804 switch (attrdef->m_size) {
807 unsigned char *val = reinterpret_cast<unsigned char*>(ptr);
808 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
813 unsigned short *val = reinterpret_cast<unsigned short*>(ptr);
814 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
819 unsigned int *val = reinterpret_cast<unsigned int*>(ptr);
820 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
824 PyErr_Format(PyExc_TypeError, "internal error: unsupported flag field \"%s\"", attrdef->m_name);
829 case KX_PYATTRIBUTE_TYPE_SHORT:
831 short int *var = reinterpret_cast<short int*>(ptr);
832 if (PyLong_Check(value))
834 long val = PyLong_AsSsize_t(value);
835 if (attrdef->m_clamp)
837 if (val < attrdef->m_imin)
838 val = attrdef->m_imin;
839 else if (val > attrdef->m_imax)
840 val = attrdef->m_imax;
842 else if (val < attrdef->m_imin || val > attrdef->m_imax)
844 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
847 *var = (short int)val;
851 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
856 case KX_PYATTRIBUTE_TYPE_ENUM:
857 // enum are equivalent to int, just make sure that the field size matches:
858 if (sizeof(int) != attrdef->m_size)
860 PyErr_Format(PyExc_AttributeError, "attribute size check error for attribute \"%s\", report to blender.org", attrdef->m_name);
864 case KX_PYATTRIBUTE_TYPE_INT:
866 int *var = reinterpret_cast<int*>(ptr);
867 if (PyLong_Check(value))
869 long val = PyLong_AsSsize_t(value);
870 if (attrdef->m_clamp)
872 if (val < attrdef->m_imin)
873 val = attrdef->m_imin;
874 else if (val > attrdef->m_imax)
875 val = attrdef->m_imax;
877 else if (val < attrdef->m_imin || val > attrdef->m_imax)
879 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
886 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
891 case KX_PYATTRIBUTE_TYPE_FLOAT:
893 float *var = reinterpret_cast<float*>(ptr);
894 if (attrdef->m_imin != 0)
896 if (attrdef->m_size != attrdef->m_imin*attrdef->m_imax*sizeof(float))
898 PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name);
901 if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imin)
903 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
906 for (int i=0; i<attrdef->m_imin; i++)
908 PyObject *list = PySequence_GetItem(value, i); /* new ref */
909 if (!PySequence_Check(list) || PySequence_Size(list) != attrdef->m_imax)
911 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
912 goto RESTORE_AND_ERROR;
914 for (int j=0; j<attrdef->m_imax; j++)
916 item = PySequence_GetItem(list, j); /* new ref */
917 if (!py_check_attr_float(var, item, attrdef))
919 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
920 goto RESTORE_AND_ERROR;
930 else if (attrdef->m_imax != 0)
932 if (attrdef->m_size != attrdef->m_imax*sizeof(float))
934 PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name);
937 if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imax)
939 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d] floats for attribute \"%s\"", attrdef->m_imax, attrdef->m_name);
942 for (int i=0; i<attrdef->m_imax; i++)
944 item = PySequence_GetItem(value, i); /* new ref */
945 if (!py_check_attr_float(var, item, attrdef))
947 goto RESTORE_AND_ERROR;
956 if (!py_check_attr_float(var, value, attrdef))
961 case KX_PYATTRIBUTE_TYPE_VECTOR:
963 if (!PySequence_Check(value) || PySequence_Size(value) != 3)
965 PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
968 MT_Vector3 *var = reinterpret_cast<MT_Vector3*>(ptr);
969 for (int i=0; i<3; i++)
971 item = PySequence_GetItem(value, i); /* new ref */
972 double val = PyFloat_AsDouble(item);
975 if (val == -1.0 && PyErr_Occurred())
977 PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
978 goto RESTORE_AND_ERROR;
980 else if (attrdef->m_clamp)
982 if (val < attrdef->m_fmin)
983 val = attrdef->m_fmin;
984 else if (val > attrdef->m_fmax)
985 val = attrdef->m_fmax;
987 else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
989 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
990 goto RESTORE_AND_ERROR;
992 (*var)[i] = (MT_Scalar)val;
996 case KX_PYATTRIBUTE_TYPE_CHAR:
998 if (PyUnicode_Check(value))
1001 char *val = _PyUnicode_AsStringAndSize(value, &val_len);
1002 strncpy(ptr, val, attrdef->m_size);
1003 ptr[attrdef->m_size-1] = 0;
1007 PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name);
1008 goto FREE_AND_ERROR;
1012 case KX_PYATTRIBUTE_TYPE_STRING:
1014 STR_String *var = reinterpret_cast<STR_String*>(ptr);
1015 if (PyUnicode_Check(value))
1018 char *val = _PyUnicode_AsStringAndSize(value, &val_len);
1019 if (attrdef->m_clamp)
1021 if (val_len < attrdef->m_imin)
1023 // can't increase the length of the string
1024 PyErr_Format(PyExc_ValueError, "string length too short for attribute \"%s\"", attrdef->m_name);
1025 goto FREE_AND_ERROR;
1027 else if (val_len > attrdef->m_imax)
1030 char c = val[attrdef->m_imax];
1031 val[attrdef->m_imax] = 0;
1033 val[attrdef->m_imax] = c;
1036 } else if (val_len < attrdef->m_imin || val_len > attrdef->m_imax)
1038 PyErr_Format(PyExc_ValueError, "string length out of range for attribute \"%s\"", attrdef->m_name);
1039 goto FREE_AND_ERROR;
1045 PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name);
1046 goto FREE_AND_ERROR;
1051 // should not happen
1052 PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
1053 goto FREE_AND_ERROR;
1056 // check if post processing is needed
1057 if (attrdef->m_checkFunction != NULL)
1059 if ((*attrdef->m_checkFunction)(ref, attrdef) != 0)
1065 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING)
1067 // special case for STR_String: restore the string
1068 STR_String *var = reinterpret_cast<STR_String*>(ptr);
1069 *var = reinterpret_cast<char*>(undoBuffer);
1073 // other field type have direct values
1074 memcpy(ptr, undoBuffer, bufferSize);
1094 /*------------------------------
1095 * PyObjectPlus repr -- representations
1096 ------------------------------*/
1097 PyObject *PyObjectPlus::py_repr(void)
1099 PyErr_SetString(PyExc_SystemError, "Representation not overridden by object.");
1103 PyObject *PyObjectPlus::GetProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr)
1105 if (self->m_proxy==NULL)
1107 self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
1108 BGE_PROXY_PYOWNS(self->m_proxy) = false;
1109 BGE_PROXY_PYREF(self->m_proxy) = true;
1111 //PyObject_Print(self->m_proxy, stdout, 0);
1112 //printf("ref %d\n", self->m_proxy->ob_refcnt);
1114 BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */
1115 BGE_PROXY_PTR(self->m_proxy) = ptr;
1116 Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */
1117 return self->m_proxy;
1120 PyObject *PyObjectPlus::NewProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr, bool py_owns)
1124 // in case of proxy without reference to game object
1125 PyObject* proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
1126 BGE_PROXY_PYREF(proxy) = false;
1127 BGE_PROXY_PYOWNS(proxy) = py_owns;
1128 BGE_PROXY_REF(proxy) = NULL;
1129 BGE_PROXY_PTR(proxy) = ptr;
1136 BGE_PROXY_REF(self->m_proxy) = NULL;
1137 Py_DECREF(self->m_proxy);
1138 self->m_proxy= NULL;
1141 Py_INCREF(self->m_proxy);
1142 return self->m_proxy;
1147 GetProxyPlus_Ext(self, tp, ptr);
1149 BGE_PROXY_PYOWNS(self->m_proxy) = py_owns;
1150 Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */
1152 return self->m_proxy;
1155 ///////////////////////////////////////////////////////////////////////////////////////////////
1156 ///////////////////////////////////////////////////////////////////////////////////////////////
1157 /* deprecation warning management */
1159 bool PyObjectPlus::m_ignore_deprecation_warnings(false);
1160 void PyObjectPlus::SetDeprecationWarnings(bool ignoreDeprecationWarnings)
1162 m_ignore_deprecation_warnings = ignoreDeprecationWarnings;
1167 // import sys; print '\t%s:%d' % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_lineno)
1169 PyObject *getframe, *frame;
1170 PyObject *f_lineno, *f_code, *co_filename;
1172 getframe = PySys_GetObject((char *)"_getframe"); // borrowed
1174 frame = PyObject_CallObject(getframe, NULL);
1176 f_lineno= PyObject_GetAttrString(frame, "f_lineno");
1177 f_code= PyObject_GetAttrString(frame, "f_code");
1178 if (f_lineno && f_code) {
1179 co_filename= PyObject_GetAttrString(f_code, "co_filename");
1182 printf("\t%s:%d\n", _PyUnicode_AsString(co_filename), (int)PyLong_AsSsize_t(f_lineno));
1184 Py_DECREF(f_lineno);
1186 Py_DECREF(co_filename);
1192 Py_XDECREF(f_lineno);
1199 printf("\tERROR - Could not access sys._getframe(0).f_lineno or sys._getframe().f_code.co_filename\n");
1202 void PyObjectPlus::ShowDeprecationWarning_func(const char* old_way,const char* new_way)
1204 printf("Method %s is deprecated, please use %s instead.\n", old_way, new_way);
1208 void PyObjectPlus::ClearDeprecationWarning()
1210 WarnLink *wlink_next;
1211 WarnLink *wlink = GetDeprecationWarningLinkFirst();
1215 wlink->warn_done= false; /* no need to NULL the link, its cleared before adding to the list next time round */
1216 wlink_next= reinterpret_cast<WarnLink *>(wlink->link);
1220 NullDeprecationWarning();
1223 WarnLink* m_base_wlink_first= NULL;
1224 WarnLink* m_base_wlink_last= NULL;
1226 WarnLink* PyObjectPlus::GetDeprecationWarningLinkFirst(void) {return m_base_wlink_first;}
1227 WarnLink* PyObjectPlus::GetDeprecationWarningLinkLast(void) {return m_base_wlink_last;}
1228 void PyObjectPlus::SetDeprecationWarningFirst(WarnLink* wlink) {m_base_wlink_first= wlink;}
1229 void PyObjectPlus::SetDeprecationWarningLinkLast(WarnLink* wlink) {m_base_wlink_last= wlink;}
1230 void PyObjectPlus::NullDeprecationWarning() {m_base_wlink_first= m_base_wlink_last= NULL;}
1232 #endif // WITH_PYTHON