PyAPI RNA/BGE
[blender.git] / source / gameengine / Expressions / PyObjectPlus.cpp
1 /**
2  * $Id$
3  * ***** BEGIN GPL LICENSE BLOCK *****
4  *
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.
9  *
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.
14  *
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  *
19  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
20  * All rights reserved.
21  *
22  * The Original Code is: all of this file.
23  *
24  * Contributor(s): none yet.
25  *
26  * ***** END GPL LICENSE BLOCK *****
27  */
28
29 #ifdef HAVE_CONFIG_H
30 #include <config.h>
31 #endif
32
33 #ifndef NO_EXP_PYTHON_EMBEDDING
34
35 /*------------------------------
36  * PyObjectPlus cpp
37  *
38  * C++ library routines for Crawl 3.2
39  *
40  * Derived from work by
41  * David Redish
42  * graduate student
43  * Computer Science Department 
44  * Carnegie Mellon University (CMU)
45  * Center for the Neural Basis of Cognition (CNBC) 
46  * http://www.python.org/doc/PyCPP.html
47  *
48 ------------------------------*/
49 #include <MT_assert.h>
50 #include "stdlib.h"
51 #include "PyObjectPlus.h"
52 #include "STR_String.h"
53 #include "MT_Vector3.h"
54 /*------------------------------
55  * PyObjectPlus Type            -- Every class, even the abstract one should have a Type
56 ------------------------------*/
57
58
59 PyTypeObject PyObjectPlus::Type = {
60 #if (PY_VERSION_HEX >= 0x02060000)
61         PyVarObject_HEAD_INIT(NULL, 0)
62 #else
63         /* python 2.5 and below */
64         PyObject_HEAD_INIT( NULL )  /* required py macro */
65         0,                              /*ob_size*/
66 #endif
67         "PyObjectPlus",                 /*tp_name*/
68         sizeof(PyObjectPlus_Proxy),             /*tp_basicsize*/
69         0,                              /*tp_itemsize*/
70         /* methods */
71         py_base_dealloc,
72         0,
73         0,
74         0,
75         0,
76         py_base_repr,
77         0,0,0,0,0,0,
78         py_base_getattro,
79         py_base_setattro,
80         0,0,0,0,0,0,0,0,0,
81         Methods
82 };
83
84
85 PyObjectPlus::~PyObjectPlus()
86 {
87         if(m_proxy) {
88                 Py_DECREF(m_proxy);                     /* Remove own reference, python may still have 1 */
89                 BGE_PROXY_REF(m_proxy)= NULL;
90         }
91 //      assert(ob_refcnt==0);
92 }
93
94 void PyObjectPlus::py_base_dealloc(PyObject *self)                              // python wrapper
95 {
96         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
97         if(self_plus) {
98                 if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it  */
99                         self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */
100                         delete self_plus;
101                 }
102                 
103                 BGE_PROXY_REF(self)= NULL; // not really needed
104         }
105         PyObject_DEL( self );
106 };
107
108 PyObjectPlus::PyObjectPlus(PyTypeObject *T) : SG_QList()                                // constructor
109 {
110         MT_assert(T != NULL);
111         m_proxy= NULL;
112 };
113   
114 /*------------------------------
115  * PyObjectPlus Methods         -- Every class, even the abstract one should have a Methods
116 ------------------------------*/
117 PyMethodDef PyObjectPlus::Methods[] = {
118   {"isA",                (PyCFunction) sPyisA,                  METH_O},
119   {NULL, NULL}          /* Sentinel */
120 };
121
122 PyAttributeDef PyObjectPlus::Attributes[] = {
123         KX_PYATTRIBUTE_RO_FUNCTION("invalid",           PyObjectPlus, pyattr_get_invalid),
124         {NULL} //Sentinel
125 };
126
127 PyObject* PyObjectPlus::pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
128 {       
129         Py_RETURN_FALSE;
130 }
131
132 /*------------------------------
133  * PyObjectPlus Parents         -- Every class, even the abstract one should have parents
134 ------------------------------*/
135 PyParentObject PyObjectPlus::Parents[] = {&PyObjectPlus::Type, NULL};
136
137 /*------------------------------
138  * PyObjectPlus attributes      -- attributes
139 ------------------------------*/
140
141
142 /* This should be the entry in Type since it takes the C++ class from PyObjectPlus_Proxy */
143 PyObject *PyObjectPlus::py_base_getattro(PyObject * self, PyObject *attr)
144 {
145         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
146         if(self_plus==NULL) {
147                 if(!strcmp("invalid", PyString_AsString(attr))) {
148                         Py_RETURN_TRUE;
149                 }
150                 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
151                 return NULL;
152         }
153         
154         PyObject *ret= self_plus->py_getattro(attr);
155         
156         /* Attribute not found, was this a __dict__ lookup?, otherwise set an error if none is set */
157         if(ret==NULL) {
158                 char *attr_str= PyString_AsString(attr);
159                 
160                 if (strcmp(attr_str, "__dict__")==0)
161                 {
162                         /* the error string will probably not
163                          * be set but just incase clear it */
164                         PyErr_Clear(); 
165                         ret= self_plus->py_getattro_dict();
166                 }
167                 else if (!PyErr_Occurred()) {
168                         /* We looked for an attribute but it wasnt found
169                          * since py_getattro didnt set the error, set it here */
170                         PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", self->ob_type->tp_name, attr_str);
171                 }
172         }
173         return ret;
174 }
175
176 /* This should be the entry in Type since it takes the C++ class from PyObjectPlus_Proxy */
177 int PyObjectPlus::py_base_setattro(PyObject *self, PyObject *attr, PyObject *value)
178 {
179         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
180         if(self_plus==NULL) {
181                 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
182                 return -1;
183         }
184         
185         if (value==NULL)
186                 return self_plus->py_delattro(attr);
187         
188         return self_plus->py_setattro(attr, value); 
189 }
190
191 PyObject *PyObjectPlus::py_base_repr(PyObject *self)                    // This should be the entry in Type.
192 {
193         
194         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
195         if(self_plus==NULL) {
196                 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
197                 return NULL;
198         }
199         
200         return self_plus->py_repr();  
201 }
202
203 PyObject *PyObjectPlus::py_getattro(PyObject* attr)
204 {
205         PyObject *descr = PyDict_GetItem(Type.tp_dict, attr); \
206         if (descr == NULL) {
207                 return NULL; /* py_base_getattro sets the error, this way we can avoid setting the error at many levels */
208         } else {
209                 /* Copied from py_getattro_up */
210                 if (PyCObject_Check(descr)) {
211                         return py_get_attrdef((void *)this, (const PyAttributeDef*)PyCObject_AsVoidPtr(descr));
212                 } else if (descr->ob_type->tp_descr_get) {
213                         return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, this->m_proxy);
214                 } else {
215                         return NULL;
216                 }
217                 /* end py_getattro_up copy */
218         }
219 }
220
221 PyObject* PyObjectPlus::py_getattro_dict() {
222         return py_getattr_dict(NULL, Type.tp_dict);
223 }
224
225 int PyObjectPlus::py_delattro(PyObject* attr)
226 {
227         PyErr_SetString(PyExc_AttributeError, "attribute cant be deleted");
228         return 1;
229 }
230
231 int PyObjectPlus::py_setattro(PyObject *attr, PyObject* value)
232 {
233         PyErr_SetString(PyExc_AttributeError, "attribute cant be set");
234         return PY_SET_ATTR_MISSING;
235 }
236
237 PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef)
238 {
239         if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
240         {
241                 // fake attribute, ignore
242                 return NULL;
243         }
244         if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
245         {
246                 // the attribute has no field correspondance, handover processing to function.
247                 if (attrdef->m_getFunction == NULL)
248                         return NULL;
249                 return (*attrdef->m_getFunction)(self, attrdef);
250         }
251         char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
252         if (attrdef->m_length > 1)
253         {
254                 PyObject* resultlist = PyList_New(attrdef->m_length);
255                 for (unsigned int i=0; i<attrdef->m_length; i++)
256                 {
257                         switch (attrdef->m_type) {
258                         case KX_PYATTRIBUTE_TYPE_BOOL:
259                                 {
260                                         bool *val = reinterpret_cast<bool*>(ptr);
261                                         ptr += sizeof(bool);
262                                         PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
263                                         break;
264                                 }
265                         case KX_PYATTRIBUTE_TYPE_SHORT:
266                                 {
267                                         short int *val = reinterpret_cast<short int*>(ptr);
268                                         ptr += sizeof(short int);
269                                         PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
270                                         break;
271                                 }
272                         case KX_PYATTRIBUTE_TYPE_ENUM:
273                                 // enum are like int, just make sure the field size is the same
274                                 if (sizeof(int) != attrdef->m_size)
275                                 {
276                                         Py_DECREF(resultlist);
277                                         return NULL;
278                                 }
279                                 // walkthrough
280                         case KX_PYATTRIBUTE_TYPE_INT:
281                                 {
282                                         int *val = reinterpret_cast<int*>(ptr);
283                                         ptr += sizeof(int);
284                                         PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
285                                         break;
286                                 }
287                         case KX_PYATTRIBUTE_TYPE_FLOAT:
288                                 {
289                                         float *val = reinterpret_cast<float*>(ptr);
290                                         ptr += sizeof(float);
291                                         PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(*val));
292                                         break;
293                                 }
294                         default:
295                                 // no support for array of complex data
296                                 Py_DECREF(resultlist);
297                                 return NULL;
298                         }
299                 }
300                 return resultlist;
301         }
302         else
303         {
304                 switch (attrdef->m_type) {
305                 case KX_PYATTRIBUTE_TYPE_BOOL:
306                         {
307                                 bool *val = reinterpret_cast<bool*>(ptr);
308                                 return PyInt_FromLong(*val);
309                         }
310                 case KX_PYATTRIBUTE_TYPE_SHORT:
311                         {
312                                 short int *val = reinterpret_cast<short int*>(ptr);
313                                 return PyInt_FromLong(*val);
314                         }
315                 case KX_PYATTRIBUTE_TYPE_ENUM:
316                         // enum are like int, just make sure the field size is the same
317                         if (sizeof(int) != attrdef->m_size)
318                         {
319                                 return NULL;
320                         }
321                         // walkthrough
322                 case KX_PYATTRIBUTE_TYPE_INT:
323                         {
324                                 int *val = reinterpret_cast<int*>(ptr);
325                                 return PyInt_FromLong(*val);
326                         }
327                 case KX_PYATTRIBUTE_TYPE_FLOAT:
328                         {
329                                 float *val = reinterpret_cast<float*>(ptr);
330                                 return PyFloat_FromDouble(*val);
331                         }
332                 case KX_PYATTRIBUTE_TYPE_VECTOR:
333                         {
334                                 MT_Vector3 *val = reinterpret_cast<MT_Vector3*>(ptr);
335 #ifdef USE_MATHUTILS
336                                 float fval[3]= {(*val)[0], (*val)[1], (*val)[2]};
337                                 return newVectorObject(fval, 3, Py_NEW);
338 #else
339                                 PyObject* resultlist = PyList_New(3);
340                                 for (unsigned int i=0; i<3; i++)
341                                 {
342                                         PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble((*val)[i]));
343                                 }
344                                 return resultlist;
345 #endif
346                         }
347                 case KX_PYATTRIBUTE_TYPE_STRING:
348                         {
349                                 STR_String *val = reinterpret_cast<STR_String*>(ptr);
350                                 return PyString_FromString(*val);
351                         }
352                 default:
353                         return NULL;
354                 }
355         }
356 }
357
358 int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyObject *value)
359 {
360         void *undoBuffer = NULL;
361         void *sourceBuffer = NULL;
362         size_t bufferSize = 0;
363         
364         char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
365         if (attrdef->m_length > 1)
366         {
367                 if (!PySequence_Check(value)) 
368                 {
369                         PyErr_Format(PyExc_TypeError, "expected a sequence for attribute \"%s\"", attrdef->m_name);
370                         return PY_SET_ATTR_FAIL;
371                 }
372                 if (PySequence_Size(value) != attrdef->m_length)
373                 {
374                         PyErr_Format(PyExc_TypeError, "incorrect number of elements in sequence for attribute \"%s\"", attrdef->m_name);
375                         return PY_SET_ATTR_FAIL;
376                 }
377                 switch (attrdef->m_type) 
378                 {
379                 case KX_PYATTRIBUTE_TYPE_FUNCTION:
380                         if (attrdef->m_setFunction == NULL) 
381                         {
382                                 PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name);
383                                 return PY_SET_ATTR_FAIL;
384                         }
385                         return (*attrdef->m_setFunction)(self, attrdef, value);
386                 case KX_PYATTRIBUTE_TYPE_BOOL:
387                         bufferSize = sizeof(bool);
388                         break;
389                 case KX_PYATTRIBUTE_TYPE_SHORT:
390                         bufferSize = sizeof(short int);
391                         break;
392                 case KX_PYATTRIBUTE_TYPE_ENUM:
393                 case KX_PYATTRIBUTE_TYPE_INT:
394                         bufferSize = sizeof(int);
395                         break;
396                 case KX_PYATTRIBUTE_TYPE_FLOAT:
397                         bufferSize = sizeof(float);
398                         break;
399                 default:
400                         // should not happen
401                         PyErr_Format(PyExc_AttributeError, "Unsupported attribute type for attribute \"%s\", report to blender.org", attrdef->m_name);
402                         return PY_SET_ATTR_FAIL;
403                 }
404                 // let's implement a smart undo method
405                 bufferSize *= attrdef->m_length;
406                 undoBuffer = malloc(bufferSize);
407                 sourceBuffer = ptr;
408                 if (undoBuffer)
409                 {
410                         memcpy(undoBuffer, sourceBuffer, bufferSize);
411                 }
412                 for (int i=0; i<attrdef->m_length; i++)
413                 {
414                         PyObject *item = PySequence_GetItem(value, i); /* new ref */
415                         // we can decrement the reference immediately, the reference count
416                         // is at least 1 because the item is part of an array
417                         Py_DECREF(item);
418                         switch (attrdef->m_type) 
419                         {
420                         case KX_PYATTRIBUTE_TYPE_BOOL:
421                                 {
422                                         bool *var = reinterpret_cast<bool*>(ptr);
423                                         ptr += sizeof(bool);
424                                         if (PyInt_Check(item)) 
425                                         {
426                                                 *var = (PyInt_AsLong(item) != 0);
427                                         } 
428                                         else if (PyBool_Check(item))
429                                         {
430                                                 *var = (item == Py_True);
431                                         }
432                                         else
433                                         {
434                                                 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
435                                                 goto UNDO_AND_ERROR;
436                                         }
437                                         break;
438                                 }
439                         case KX_PYATTRIBUTE_TYPE_SHORT:
440                                 {
441                                         short int *var = reinterpret_cast<short int*>(ptr);
442                                         ptr += sizeof(short int);
443                                         if (PyInt_Check(item)) 
444                                         {
445                                                 long val = PyInt_AsLong(item);
446                                                 if (attrdef->m_clamp)
447                                                 {
448                                                         if (val < attrdef->m_imin)
449                                                                 val = attrdef->m_imin;
450                                                         else if (val > attrdef->m_imax)
451                                                                 val = attrdef->m_imax;
452                                                 }
453                                                 else if (val < attrdef->m_imin || val > attrdef->m_imax)
454                                                 {
455                                                         PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
456                                                         goto UNDO_AND_ERROR;
457                                                 }
458                                                 *var = (short int)val;
459                                         }
460                                         else
461                                         {
462                                                 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
463                                                 goto UNDO_AND_ERROR;
464                                         }
465                                         break;
466                                 }
467                         case KX_PYATTRIBUTE_TYPE_ENUM:
468                                 // enum are equivalent to int, just make sure that the field size matches:
469                                 if (sizeof(int) != attrdef->m_size)
470                                 {
471                                         PyErr_Format(PyExc_AttributeError, "Size check error for attribute, \"%s\", report to blender.org", attrdef->m_name);
472                                         goto UNDO_AND_ERROR;
473                                 }
474                                 // walkthrough
475                         case KX_PYATTRIBUTE_TYPE_INT:
476                                 {
477                                         int *var = reinterpret_cast<int*>(ptr);
478                                         ptr += sizeof(int);
479                                         if (PyInt_Check(item)) 
480                                         {
481                                                 long val = PyInt_AsLong(item);
482                                                 if (attrdef->m_clamp)
483                                                 {
484                                                         if (val < attrdef->m_imin)
485                                                                 val = attrdef->m_imin;
486                                                         else if (val > attrdef->m_imax)
487                                                                 val = attrdef->m_imax;
488                                                 }
489                                                 else if (val < attrdef->m_imin || val > attrdef->m_imax)
490                                                 {
491                                                         PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
492                                                         goto UNDO_AND_ERROR;
493                                                 }
494                                                 *var = (int)val;
495                                         }
496                                         else
497                                         {
498                                                 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
499                                                 goto UNDO_AND_ERROR;
500                                         }
501                                         break;
502                                 }
503                         case KX_PYATTRIBUTE_TYPE_FLOAT:
504                                 {
505                                         float *var = reinterpret_cast<float*>(ptr);
506                                         ptr += sizeof(float);
507                                         double val = PyFloat_AsDouble(item);
508                                         if (val == -1.0 && PyErr_Occurred())
509                                         {
510                                                 PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
511                                                 goto UNDO_AND_ERROR;
512                                         }
513                                         else if (attrdef->m_clamp) 
514                                         {
515                                                 if (val < attrdef->m_fmin)
516                                                         val = attrdef->m_fmin;
517                                                 else if (val > attrdef->m_fmax)
518                                                         val = attrdef->m_fmax;
519                                         }
520                                         else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
521                                         {
522                                                 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
523                                                 goto UNDO_AND_ERROR;
524                                         }
525                                         *var = (float)val;
526                                         break;
527                                 }
528                         default:
529                                 // should not happen
530                                 PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name);
531                                 goto UNDO_AND_ERROR;
532                         }
533                 }
534                 // no error, call check function if any
535                 if (attrdef->m_checkFunction != NULL)
536                 {
537                         if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
538                         {
539                                 // if the checing function didnt set an error then set a generic one here so we dont set an error with no exception
540                                 if (PyErr_Occurred()==0)
541                                         PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", reasion unknown", attrdef->m_name);
542                                 
543                                 // post check returned an error, restore values
544                         UNDO_AND_ERROR:
545                                 if (undoBuffer)
546                                 {
547                                         memcpy(sourceBuffer, undoBuffer, bufferSize);
548                                         free(undoBuffer);
549                                 }
550                                 return PY_SET_ATTR_FAIL;
551                         }
552                 }
553                 if (undoBuffer)
554                         free(undoBuffer);
555                 return PY_SET_ATTR_SUCCESS;
556         }
557         else    // simple attribute value
558         {
559                 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
560                 {
561                         if (attrdef->m_setFunction == NULL)
562                         {
563                                 PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name);
564                                 return PY_SET_ATTR_FAIL;
565                         }
566                         return (*attrdef->m_setFunction)(self, attrdef, value);
567                 }
568                 if (attrdef->m_checkFunction != NULL || attrdef->m_type == KX_PYATTRIBUTE_TYPE_VECTOR)
569                 {
570                         // post check function is provided, prepare undo buffer
571                         sourceBuffer = ptr;
572                         switch (attrdef->m_type) 
573                         {
574                         case KX_PYATTRIBUTE_TYPE_BOOL:
575                                 bufferSize = sizeof(bool);
576                                 break;
577                         case KX_PYATTRIBUTE_TYPE_SHORT:
578                                 bufferSize = sizeof(short);
579                                 break;
580                         case KX_PYATTRIBUTE_TYPE_ENUM:
581                         case KX_PYATTRIBUTE_TYPE_INT:
582                                 bufferSize = sizeof(int);
583                                 break;
584                         case KX_PYATTRIBUTE_TYPE_FLOAT:
585                                 bufferSize = sizeof(float);
586                                 break;
587                         case KX_PYATTRIBUTE_TYPE_STRING:
588                                 sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr();
589                                 if (sourceBuffer)
590                                         bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1;
591                                 break;
592                         case KX_PYATTRIBUTE_TYPE_VECTOR:
593                                 bufferSize = sizeof(MT_Vector3);
594                                 break;
595                         default:
596                                 PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
597                                 return PY_SET_ATTR_FAIL;
598                         }
599                         if (bufferSize)
600                         {
601                                 undoBuffer = malloc(bufferSize);
602                                 if (undoBuffer)
603                                 {
604                                         memcpy(undoBuffer, sourceBuffer, bufferSize);
605                                 }
606                         }
607                 }
608                         
609                 switch (attrdef->m_type) 
610                 {
611                 case KX_PYATTRIBUTE_TYPE_BOOL:
612                         {
613                                 bool *var = reinterpret_cast<bool*>(ptr);
614                                 if (PyInt_Check(value)) 
615                                 {
616                                         *var = (PyInt_AsLong(value) != 0);
617                                 } 
618                                 else if (PyBool_Check(value))
619                                 {
620                                         *var = (value == Py_True);
621                                 }
622                                 else
623                                 {
624                                         PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
625                                         goto FREE_AND_ERROR;
626                                 }
627                                 break;
628                         }
629                 case KX_PYATTRIBUTE_TYPE_SHORT:
630                         {
631                                 short int *var = reinterpret_cast<short int*>(ptr);
632                                 if (PyInt_Check(value)) 
633                                 {
634                                         long val = PyInt_AsLong(value);
635                                         if (attrdef->m_clamp)
636                                         {
637                                                 if (val < attrdef->m_imin)
638                                                         val = attrdef->m_imin;
639                                                 else if (val > attrdef->m_imax)
640                                                         val = attrdef->m_imax;
641                                         }
642                                         else if (val < attrdef->m_imin || val > attrdef->m_imax)
643                                         {
644                                                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
645                                                 goto FREE_AND_ERROR;
646                                         }
647                                         *var = (short int)val;
648                                 }
649                                 else
650                                 {
651                                         PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
652                                         goto FREE_AND_ERROR;
653                                 }
654                                 break;
655                         }
656                 case KX_PYATTRIBUTE_TYPE_ENUM:
657                         // enum are equivalent to int, just make sure that the field size matches:
658                         if (sizeof(int) != attrdef->m_size)
659                         {
660                                 PyErr_Format(PyExc_AttributeError, "attribute size check error for attribute \"%s\", report to blender.org", attrdef->m_name);
661                                 goto FREE_AND_ERROR;
662                         }
663                         // walkthrough
664                 case KX_PYATTRIBUTE_TYPE_INT:
665                         {
666                                 int *var = reinterpret_cast<int*>(ptr);
667                                 if (PyInt_Check(value)) 
668                                 {
669                                         long val = PyInt_AsLong(value);
670                                         if (attrdef->m_clamp)
671                                         {
672                                                 if (val < attrdef->m_imin)
673                                                         val = attrdef->m_imin;
674                                                 else if (val > attrdef->m_imax)
675                                                         val = attrdef->m_imax;
676                                         }
677                                         else if (val < attrdef->m_imin || val > attrdef->m_imax)
678                                         {
679                                                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
680                                                 goto FREE_AND_ERROR;
681                                         }
682                                         *var = (int)val;
683                                 }
684                                 else
685                                 {
686                                         PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
687                                         goto FREE_AND_ERROR;
688                                 }
689                                 break;
690                         }
691                 case KX_PYATTRIBUTE_TYPE_FLOAT:
692                         {
693                                 float *var = reinterpret_cast<float*>(ptr);
694                                 double val = PyFloat_AsDouble(value);
695                                 if (val == -1.0 && PyErr_Occurred())
696                                 {
697                                         PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
698                                         goto FREE_AND_ERROR;
699                                 }
700                                 else if (attrdef->m_clamp)
701                                 {
702                                         if (val < attrdef->m_fmin)
703                                                 val = attrdef->m_fmin;
704                                         else if (val > attrdef->m_fmax)
705                                                 val = attrdef->m_fmax;
706                                 }
707                                 else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
708                                 {
709                                         PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
710                                         goto FREE_AND_ERROR;
711                                 }
712                                 *var = (float)val;
713                                 break;
714                         }
715                 case KX_PYATTRIBUTE_TYPE_VECTOR:
716                         {
717                                 if (!PySequence_Check(value) || PySequence_Size(value) != 3) 
718                                 {
719                                         PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
720                                         return PY_SET_ATTR_FAIL;
721                                 }
722                                 MT_Vector3 *var = reinterpret_cast<MT_Vector3*>(ptr);
723                                 for (int i=0; i<3; i++)
724                                 {
725                                         PyObject *item = PySequence_GetItem(value, i); /* new ref */
726                                         // we can decrement the reference immediately, the reference count
727                                         // is at least 1 because the item is part of an array
728                                         Py_DECREF(item);
729                                         double val = PyFloat_AsDouble(item);
730                                         if (val == -1.0 && PyErr_Occurred())
731                                         {
732                                                 PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
733                                                 goto RESTORE_AND_ERROR;
734                                         }
735                                         else if (attrdef->m_clamp)
736                                         {
737                                                 if (val < attrdef->m_fmin)
738                                                         val = attrdef->m_fmin;
739                                                 else if (val > attrdef->m_fmax)
740                                                         val = attrdef->m_fmax;
741                                         }
742                                         else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
743                                         {
744                                                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
745                                                 goto RESTORE_AND_ERROR;
746                                         }
747                                         (*var)[i] = (MT_Scalar)val;
748                                 }
749                                 break;
750                         }
751                 case KX_PYATTRIBUTE_TYPE_STRING:
752                         {
753                                 STR_String *var = reinterpret_cast<STR_String*>(ptr);
754                                 if (PyString_Check(value)) 
755                                 {
756                                         char *val = PyString_AsString(value);
757                                         if (attrdef->m_clamp)
758                                         {
759                                                 if (strlen(val) < attrdef->m_imin)
760                                                 {
761                                                         // can't increase the length of the string
762                                                         PyErr_Format(PyExc_ValueError, "string length too short for attribute \"%s\"", attrdef->m_name);
763                                                         goto FREE_AND_ERROR;
764                                                 }
765                                                 else if (strlen(val) > attrdef->m_imax)
766                                                 {
767                                                         // trim the string
768                                                         char c = val[attrdef->m_imax];
769                                                         val[attrdef->m_imax] = 0;
770                                                         *var = val;
771                                                         val[attrdef->m_imax] = c;
772                                                         break;
773                                                 }
774                                         } else if (strlen(val) < attrdef->m_imin || strlen(val) > attrdef->m_imax)
775                                         {
776                                                 PyErr_Format(PyExc_ValueError, "string length out of range for attribute \"%s\"", attrdef->m_name);
777                                                 goto FREE_AND_ERROR;
778                                         }
779                                         *var = val;
780                                 }
781                                 else
782                                 {
783                                         PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name);
784                                         goto FREE_AND_ERROR;
785                                 }
786                                 break;
787                         }
788                 default:
789                         // should not happen
790                         PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
791                         goto FREE_AND_ERROR;
792                 }
793         }
794         // check if post processing is needed
795         if (attrdef->m_checkFunction != NULL)
796         {
797                 if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
798                 {
799                         // restore value
800                 RESTORE_AND_ERROR:
801                         if (undoBuffer)
802                         {
803                                 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING)
804                                 {
805                                         // special case for STR_String: restore the string
806                                         STR_String *var = reinterpret_cast<STR_String*>(ptr);
807                                         *var = reinterpret_cast<char*>(undoBuffer);
808                                 }
809                                 else
810                                 {
811                                         // other field type have direct values
812                                         memcpy(ptr, undoBuffer, bufferSize);
813                                 }
814                         }
815                 FREE_AND_ERROR:
816                         if (undoBuffer)
817                                 free(undoBuffer);
818                         return 1;
819                 }
820         }
821         if (undoBuffer)
822                 free(undoBuffer);
823         return 0;       
824 }
825
826
827
828 /*------------------------------
829  * PyObjectPlus repr            -- representations
830 ------------------------------*/
831 PyObject *PyObjectPlus::py_repr(void)
832 {
833         PyErr_SetString(PyExc_SystemError, "Representation not overridden by object.");  
834         return NULL;
835 }
836
837 /*------------------------------
838  * PyObjectPlus isA             -- the isA functions
839 ------------------------------*/
840 bool PyObjectPlus::isA(PyTypeObject *T)         // if called with a Type, use "typename"
841 {
842         int i;
843         PyParentObject  P;
844         PyParentObject *Ps = GetParents();
845
846         for (P = Ps[i=0]; P != NULL; P = Ps[i++])
847                 if (P==T)
848                         return true;
849
850         return false;
851 }
852
853
854 bool PyObjectPlus::isA(const char *mytypename)          // check typename of each parent
855 {
856         int i;
857         PyParentObject  P;
858         PyParentObject *Ps = GetParents();
859   
860         for (P = Ps[i=0]; P != NULL; P = Ps[i++])
861                 if (strcmp(P->tp_name, mytypename)==0)
862                         return true;
863
864         return false;
865 }
866
867 PyObject *PyObjectPlus::PyisA(PyObject *value)          // Python wrapper for isA
868 {
869         if (PyType_Check(value)) {
870                 return PyBool_FromLong(isA((PyTypeObject *)value));
871         } else if (PyString_Check(value)) {
872                 return PyBool_FromLong(isA(PyString_AsString(value)));
873         }
874     PyErr_SetString(PyExc_TypeError, "object.isA(value): expected a type or a string");
875     return NULL;        
876 }
877
878
879 void PyObjectPlus::ProcessReplica()
880 {
881         /* Clear the proxy, will be created again if needed with GetProxy()
882          * otherwise the PyObject will point to the wrong reference */
883         m_proxy= NULL;
884 }
885
886 /* Sometimes we might want to manually invalidate a BGE type even if
887  * it hasnt been released by the BGE, say for example when an object
888  * is removed from a scene, accessing it may cause problems.
889  * 
890  * In this case the current proxy is made invalid, disowned,
891  * and will raise an error on access. However if python can get access
892  * to this class again it will make a new proxy and work as expected.
893  */
894 void PyObjectPlus::InvalidateProxy()            // check typename of each parent
895 {
896         if(m_proxy) { 
897                 BGE_PROXY_REF(m_proxy)=NULL;
898                 Py_DECREF(m_proxy);
899                 m_proxy= NULL;
900         }
901 }
902
903 /* Utility function called by the macro py_getattro_up()
904  * for getting ob.__dict__() values from our PyObject
905  * this is used by python for doing dir() on an object, so its good
906  * if we return a list of attributes and methods.
907  * 
908  * Other then making dir() useful the value returned from __dict__() is not useful
909  * since every value is a Py_None
910  * */
911 PyObject *py_getattr_dict(PyObject *pydict, PyObject *tp_dict)
912 {
913     if(pydict==NULL) { /* incase calling __dict__ on the parent of this object raised an error */
914         PyErr_Clear();
915         pydict = PyDict_New();
916     }
917         
918         PyDict_Update(pydict, tp_dict);
919         return pydict;
920 }
921
922
923
924 PyObject *PyObjectPlus::GetProxy_Ext(PyObjectPlus *self, PyTypeObject *tp)
925 {
926         if (self->m_proxy==NULL)
927         {
928                 self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
929                 BGE_PROXY_PYOWNS(self->m_proxy) = false;
930         }
931         //PyObject_Print(self->m_proxy, stdout, 0);
932         //printf("ref %d\n", self->m_proxy->ob_refcnt);
933         
934         BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */
935         Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */
936         return self->m_proxy;
937 }
938
939 PyObject *PyObjectPlus::NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool py_owns)
940 {
941         if (self->m_proxy)
942         {
943                 if(py_owns)
944                 {       /* Free */
945                         BGE_PROXY_REF(self->m_proxy) = NULL;
946                         Py_DECREF(self->m_proxy);
947                         self->m_proxy= NULL;
948                 }
949                 else {
950                         Py_INCREF(self->m_proxy);
951                         return self->m_proxy;
952                 }
953                 
954         }
955         
956         GetProxy_Ext(self, tp);
957         if(py_owns) {
958                 BGE_PROXY_PYOWNS(self->m_proxy) = py_owns;
959                 Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */
960         }
961         return self->m_proxy;
962 }
963
964 ///////////////////////////////////////////////////////////////////////////////////////////////
965 ///////////////////////////////////////////////////////////////////////////////////////////////
966 /* deprecation warning management */
967
968 bool PyObjectPlus::m_ignore_deprecation_warnings(false);
969 void PyObjectPlus::SetDeprecationWarnings(bool ignoreDeprecationWarnings)
970 {
971         m_ignore_deprecation_warnings = ignoreDeprecationWarnings;
972 }
973
974 void PyObjectPlus::ShowDeprecationWarning_func(const char* old_way,const char* new_way)
975 {
976         {
977                 printf("Method %s is deprecated, please use %s instead.\n", old_way, new_way);
978                 
979                 // import sys; print '\t%s:%d' % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_lineno)
980                 
981                 PyObject *getframe, *frame;
982                 PyObject *f_lineno, *f_code, *co_filename;
983                 
984                 getframe = PySys_GetObject((char *)"_getframe"); // borrowed
985                 if (getframe) {
986                         frame = PyObject_CallObject(getframe, NULL);
987                         if (frame) {
988                                 f_lineno= PyObject_GetAttrString(frame, "f_lineno");
989                                 f_code= PyObject_GetAttrString(frame, "f_code");
990                                 if (f_lineno && f_code) {
991                                         co_filename= PyObject_GetAttrString(f_code, "co_filename");
992                                         if (co_filename) {
993                                                 
994                                                 printf("\t%s:%d\n", PyString_AsString(co_filename), (int)PyInt_AsLong(f_lineno));
995                                                 
996                                                 Py_DECREF(f_lineno);
997                                                 Py_DECREF(f_code);
998                                                 Py_DECREF(co_filename);
999                                                 Py_DECREF(frame);
1000                                                 return;
1001                                         }
1002                                 }
1003                                 
1004                                 Py_XDECREF(f_lineno);
1005                                 Py_XDECREF(f_code);
1006                                 Py_DECREF(frame);
1007                         }
1008                         
1009                 }
1010                 PyErr_Clear();
1011                 printf("\tERROR - Could not access sys._getframe(0).f_lineno or sys._getframe().f_code.co_filename\n");
1012         }
1013 }
1014
1015 void PyObjectPlus::ClearDeprecationWarning()
1016 {
1017         WarnLink *wlink_next;
1018         WarnLink *wlink = GetDeprecationWarningLinkFirst();
1019         
1020         while(wlink)
1021         {
1022                 wlink->warn_done= false; /* no need to NULL the link, its cleared before adding to the list next time round */
1023                 wlink_next= reinterpret_cast<WarnLink *>(wlink->link);
1024                 wlink->link= NULL;
1025                 wlink= wlink_next;
1026         }
1027         NullDeprecationWarning();
1028 }
1029
1030 WarnLink*               m_base_wlink_first= NULL;
1031 WarnLink*               m_base_wlink_last= NULL;
1032
1033 WarnLink*               PyObjectPlus::GetDeprecationWarningLinkFirst(void) {return m_base_wlink_first;}
1034 WarnLink*               PyObjectPlus::GetDeprecationWarningLinkLast(void) {return m_base_wlink_last;}
1035 void                    PyObjectPlus::SetDeprecationWarningFirst(WarnLink* wlink) {m_base_wlink_first= wlink;}
1036 void                    PyObjectPlus::SetDeprecationWarningLinkLast(WarnLink* wlink) {m_base_wlink_last= wlink;}
1037 void                    PyObjectPlus::NullDeprecationWarning() {m_base_wlink_first= m_base_wlink_last= NULL;}
1038
1039 #endif //NO_EXP_PYTHON_EMBEDDING
1040