BGE C++ API
[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 /*------------------------------
54  * PyObjectPlus Type            -- Every class, even the abstract one should have a Type
55 ------------------------------*/
56
57
58 PyTypeObject PyObjectPlus::Type = {
59         PyObject_HEAD_INIT(NULL)
60         0,                              /*ob_size*/
61         "PyObjectPlus",                 /*tp_name*/
62         sizeof(PyObjectPlus_Proxy),             /*tp_basicsize*/
63         0,                              /*tp_itemsize*/
64         /* methods */
65         py_base_dealloc,
66         0,
67         0,
68         0,
69         0,
70         py_base_repr,
71         0,0,0,0,0,0,
72         py_base_getattro,
73         py_base_setattro,
74         0,0,0,0,0,0,0,0,0,
75         Methods
76 };
77
78
79 PyObjectPlus::~PyObjectPlus()
80 {
81         if(m_proxy) {
82                 Py_DECREF(m_proxy);                     /* Remove own reference, python may still have 1 */
83                 BGE_PROXY_REF(m_proxy)= NULL;
84         }
85 //      assert(ob_refcnt==0);
86 }
87
88 void PyObjectPlus::py_base_dealloc(PyObject *self)                              // python wrapper
89 {
90         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
91         if(self_plus) {
92                 if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it  */
93                         delete self_plus;
94                 }
95                 
96                 BGE_PROXY_REF(self)= NULL; // not really needed
97         }
98         PyObject_DEL( self );
99 };
100
101 PyObjectPlus::PyObjectPlus(PyTypeObject *T)                             // constructor
102 {
103         MT_assert(T != NULL);
104         m_proxy= NULL;
105 };
106   
107 /*------------------------------
108  * PyObjectPlus Methods         -- Every class, even the abstract one should have a Methods
109 ------------------------------*/
110 PyMethodDef PyObjectPlus::Methods[] = {
111   {"isA",                (PyCFunction) sPyisA,                  METH_O},
112   {NULL, NULL}          /* Sentinel */
113 };
114
115 PyAttributeDef PyObjectPlus::Attributes[] = {
116         KX_PYATTRIBUTE_RO_FUNCTION("isValid",           PyObjectPlus, pyattr_get_is_valid),
117         {NULL} //Sentinel
118 };
119
120 PyObject* PyObjectPlus::pyattr_get_is_valid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
121 {       
122         Py_RETURN_TRUE;
123 }
124
125 /*------------------------------
126  * PyObjectPlus Parents         -- Every class, even the abstract one should have parents
127 ------------------------------*/
128 PyParentObject PyObjectPlus::Parents[] = {&PyObjectPlus::Type, NULL};
129
130 /*------------------------------
131  * PyObjectPlus attributes      -- attributes
132 ------------------------------*/
133
134
135 /* This should be the entry in Type since it takes the C++ class from PyObjectPlus_Proxy */
136 PyObject *PyObjectPlus::py_base_getattro(PyObject * self, PyObject *attr)
137 {
138         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
139         if(self_plus==NULL) {
140                 if(!strcmp("isValid", PyString_AsString(attr))) {
141                         Py_RETURN_TRUE;
142                 }
143                 PyErr_SetString(PyExc_RuntimeError, BGE_PROXY_ERROR_MSG);
144                 return NULL;
145         }
146         
147         PyObject *ret= self_plus->py_getattro(attr);
148         
149         /* Attribute not found, was this a __dict__ lookup?, otherwise set an error if none is set */
150         if(ret==NULL) {
151                 char *attr_str= PyString_AsString(attr);
152                 
153                 if (strcmp(attr_str, "__dict__")==0)
154                 {
155                         /* the error string will probably not
156                          * be set but just incase clear it */
157                         PyErr_Clear(); 
158                         ret= self_plus->py_getattro_dict();
159                 }
160                 else if (!PyErr_Occurred()) {
161                         /* We looked for an attribute but it wasnt found
162                          * since py_getattro didnt set the error, set it here */
163                         PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", self->ob_type->tp_name, attr_str);
164                 }
165         }
166         return ret;
167 }
168
169 /* This should be the entry in Type since it takes the C++ class from PyObjectPlus_Proxy */
170 int PyObjectPlus::py_base_setattro(PyObject *self, PyObject *attr, PyObject *value)
171 {
172         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
173         if(self_plus==NULL) {
174                 PyErr_SetString(PyExc_RuntimeError, BGE_PROXY_ERROR_MSG);
175                 return -1;
176         }
177         
178         if (value==NULL)
179                 return self_plus->py_delattro(attr);
180         
181         return self_plus->py_setattro(attr, value); 
182 }
183
184 PyObject *PyObjectPlus::py_base_repr(PyObject *self)                    // This should be the entry in Type.
185 {
186         
187         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
188         if(self_plus==NULL) {
189                 PyErr_SetString(PyExc_RuntimeError, BGE_PROXY_ERROR_MSG);
190                 return NULL;
191         }
192         
193         return self_plus->py_repr();  
194 }
195
196 PyObject *PyObjectPlus::py_getattro(PyObject* attr)
197 {
198         PyObject *descr = PyDict_GetItem(Type.tp_dict, attr); \
199         if (descr == NULL) {
200                 return NULL; /* py_base_getattro sets the error, this way we can avoid setting the error at many levels */
201         } else {
202                 /* Copied from py_getattro_up */
203                 if (PyCObject_Check(descr)) {
204                         return py_get_attrdef((void *)this, (const PyAttributeDef*)PyCObject_AsVoidPtr(descr));
205                 } else if (descr->ob_type->tp_descr_get) {
206                         return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, this->m_proxy);
207                 } else {
208                         return NULL;
209                 }
210                 /* end py_getattro_up copy */
211         }
212 }
213
214 PyObject* PyObjectPlus::py_getattro_dict() {
215         return py_getattr_dict(NULL, Type.tp_dict);
216 }
217
218 int PyObjectPlus::py_delattro(PyObject* attr)
219 {
220         PyErr_SetString(PyExc_AttributeError, "attribute cant be deleted");
221         return 1;
222 }
223
224 int PyObjectPlus::py_setattro(PyObject *attr, PyObject* value)
225 {
226         PyErr_SetString(PyExc_AttributeError, "attribute cant be set");
227         return PY_SET_ATTR_MISSING;
228 }
229
230 PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef)
231 {
232         if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
233         {
234                 // fake attribute, ignore
235                 return NULL;
236         }
237         if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
238         {
239                 // the attribute has no field correspondance, handover processing to function.
240                 if (attrdef->m_getFunction == NULL)
241                         return NULL;
242                 return (*attrdef->m_getFunction)(self, attrdef);
243         }
244         char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
245         if (attrdef->m_length > 1)
246         {
247                 PyObject* resultlist = PyList_New(attrdef->m_length);
248                 for (unsigned int i=0; i<attrdef->m_length; i++)
249                 {
250                         switch (attrdef->m_type) {
251                         case KX_PYATTRIBUTE_TYPE_BOOL:
252                                 {
253                                         bool *val = reinterpret_cast<bool*>(ptr);
254                                         ptr += sizeof(bool);
255                                         PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
256                                         break;
257                                 }
258                         case KX_PYATTRIBUTE_TYPE_SHORT:
259                                 {
260                                         short int *val = reinterpret_cast<short int*>(ptr);
261                                         ptr += sizeof(short int);
262                                         PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
263                                         break;
264                                 }
265                         case KX_PYATTRIBUTE_TYPE_ENUM:
266                                 // enum are like int, just make sure the field size is the same
267                                 if (sizeof(int) != attrdef->m_size)
268                                 {
269                                         Py_DECREF(resultlist);
270                                         return NULL;
271                                 }
272                                 // walkthrough
273                         case KX_PYATTRIBUTE_TYPE_INT:
274                                 {
275                                         int *val = reinterpret_cast<int*>(ptr);
276                                         ptr += sizeof(int);
277                                         PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
278                                         break;
279                                 }
280                         case KX_PYATTRIBUTE_TYPE_FLOAT:
281                                 {
282                                         float *val = reinterpret_cast<float*>(ptr);
283                                         ptr += sizeof(float);
284                                         PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(*val));
285                                         break;
286                                 }
287                         default:
288                                 // no support for array of complex data
289                                 Py_DECREF(resultlist);
290                                 return NULL;
291                         }
292                 }
293                 return resultlist;
294         }
295         else
296         {
297                 switch (attrdef->m_type) {
298                 case KX_PYATTRIBUTE_TYPE_BOOL:
299                         {
300                                 bool *val = reinterpret_cast<bool*>(ptr);
301                                 return PyInt_FromLong(*val);
302                         }
303                 case KX_PYATTRIBUTE_TYPE_SHORT:
304                         {
305                                 short int *val = reinterpret_cast<short int*>(ptr);
306                                 return PyInt_FromLong(*val);
307                         }
308                 case KX_PYATTRIBUTE_TYPE_ENUM:
309                         // enum are like int, just make sure the field size is the same
310                         if (sizeof(int) != attrdef->m_size)
311                         {
312                                 return NULL;
313                         }
314                         // walkthrough
315                 case KX_PYATTRIBUTE_TYPE_INT:
316                         {
317                                 int *val = reinterpret_cast<int*>(ptr);
318                                 return PyInt_FromLong(*val);
319                         }
320                 case KX_PYATTRIBUTE_TYPE_FLOAT:
321                         {
322                                 float *val = reinterpret_cast<float*>(ptr);
323                                 return PyFloat_FromDouble(*val);
324                         }
325                 case KX_PYATTRIBUTE_TYPE_STRING:
326                         {
327                                 STR_String *val = reinterpret_cast<STR_String*>(ptr);
328                                 return PyString_FromString(*val);
329                         }
330                 default:
331                         return NULL;
332                 }
333         }
334 }
335
336 int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyObject *value)
337 {
338         void *undoBuffer = NULL;
339         void *sourceBuffer = NULL;
340         size_t bufferSize = 0;
341         
342         char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
343         if (attrdef->m_length > 1)
344         {
345                 if (!PySequence_Check(value)) 
346                 {
347                         PyErr_Format(PyExc_TypeError, "expected a sequence for attribute \"%s\"", attrdef->m_name);
348                         return 1;
349                 }
350                 if (PySequence_Size(value) != attrdef->m_length)
351                 {
352                         PyErr_Format(PyExc_TypeError, "incorrect number of elements in sequence for attribute \"%s\"", attrdef->m_name);
353                         return 1;
354                 }
355                 switch (attrdef->m_type) 
356                 {
357                 case KX_PYATTRIBUTE_TYPE_FUNCTION:
358                         if (attrdef->m_setFunction == NULL) 
359                         {
360                                 PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name);
361                                 return 1;
362                         }
363                         return (*attrdef->m_setFunction)(self, attrdef, value);
364                 case KX_PYATTRIBUTE_TYPE_BOOL:
365                         bufferSize = sizeof(bool);
366                         break;
367                 case KX_PYATTRIBUTE_TYPE_SHORT:
368                         bufferSize = sizeof(short int);
369                         break;
370                 case KX_PYATTRIBUTE_TYPE_ENUM:
371                 case KX_PYATTRIBUTE_TYPE_INT:
372                         bufferSize = sizeof(int);
373                         break;
374                 case KX_PYATTRIBUTE_TYPE_FLOAT:
375                         bufferSize = sizeof(float);
376                         break;
377                 default:
378                         // should not happen
379                         PyErr_Format(PyExc_AttributeError, "Unsupported attribute type for attribute \"%s\", report to blender.org", attrdef->m_name);
380                         return 1;
381                 }
382                 // let's implement a smart undo method
383                 bufferSize *= attrdef->m_length;
384                 undoBuffer = malloc(bufferSize);
385                 sourceBuffer = ptr;
386                 if (undoBuffer)
387                 {
388                         memcpy(undoBuffer, sourceBuffer, bufferSize);
389                 }
390                 for (int i=0; i<attrdef->m_length; i++)
391                 {
392                         PyObject *item = PySequence_GetItem(value, i); /* new ref */
393                         // we can decrement the reference immediately, the reference count
394                         // is at least 1 because the item is part of an array
395                         Py_DECREF(item);
396                         switch (attrdef->m_type) 
397                         {
398                         case KX_PYATTRIBUTE_TYPE_BOOL:
399                                 {
400                                         bool *var = reinterpret_cast<bool*>(ptr);
401                                         ptr += sizeof(bool);
402                                         if (PyInt_Check(item)) 
403                                         {
404                                                 *var = (PyInt_AsLong(item) != 0);
405                                         } 
406                                         else if (PyBool_Check(item))
407                                         {
408                                                 *var = (item == Py_True);
409                                         }
410                                         else
411                                         {
412                                                 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
413                                                 goto UNDO_AND_ERROR;
414                                         }
415                                         break;
416                                 }
417                         case KX_PYATTRIBUTE_TYPE_SHORT:
418                                 {
419                                         short int *var = reinterpret_cast<short int*>(ptr);
420                                         ptr += sizeof(short int);
421                                         if (PyInt_Check(item)) 
422                                         {
423                                                 long val = PyInt_AsLong(item);
424                                                 if (attrdef->m_clamp)
425                                                 {
426                                                         if (val < attrdef->m_imin)
427                                                                 val = attrdef->m_imin;
428                                                         else if (val > attrdef->m_imax)
429                                                                 val = attrdef->m_imax;
430                                                 }
431                                                 else if (val < attrdef->m_imin || val > attrdef->m_imax)
432                                                 {
433                                                         PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
434                                                         goto UNDO_AND_ERROR;
435                                                 }
436                                                 *var = (short int)val;
437                                         }
438                                         else
439                                         {
440                                                 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
441                                                 goto UNDO_AND_ERROR;
442                                         }
443                                         break;
444                                 }
445                         case KX_PYATTRIBUTE_TYPE_ENUM:
446                                 // enum are equivalent to int, just make sure that the field size matches:
447                                 if (sizeof(int) != attrdef->m_size)
448                                 {
449                                         PyErr_Format(PyExc_AttributeError, "Size check error for attribute, \"%s\", report to blender.org", attrdef->m_name);
450                                         goto UNDO_AND_ERROR;
451                                 }
452                                 // walkthrough
453                         case KX_PYATTRIBUTE_TYPE_INT:
454                                 {
455                                         int *var = reinterpret_cast<int*>(ptr);
456                                         ptr += sizeof(int);
457                                         if (PyInt_Check(item)) 
458                                         {
459                                                 long val = PyInt_AsLong(item);
460                                                 if (attrdef->m_clamp)
461                                                 {
462                                                         if (val < attrdef->m_imin)
463                                                                 val = attrdef->m_imin;
464                                                         else if (val > attrdef->m_imax)
465                                                                 val = attrdef->m_imax;
466                                                 }
467                                                 else if (val < attrdef->m_imin || val > attrdef->m_imax)
468                                                 {
469                                                         PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
470                                                         goto UNDO_AND_ERROR;
471                                                 }
472                                                 *var = (int)val;
473                                         }
474                                         else
475                                         {
476                                                 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
477                                                 goto UNDO_AND_ERROR;
478                                         }
479                                         break;
480                                 }
481                         case KX_PYATTRIBUTE_TYPE_FLOAT:
482                                 {
483                                         float *var = reinterpret_cast<float*>(ptr);
484                                         ptr += sizeof(float);
485                                         double val = PyFloat_AsDouble(item);
486                                         if (val == -1.0 && PyErr_Occurred())
487                                         {
488                                                 PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
489                                                 goto UNDO_AND_ERROR;
490                                         }
491                                         else if (attrdef->m_clamp) 
492                                         {
493                                                 if (val < attrdef->m_fmin)
494                                                         val = attrdef->m_fmin;
495                                                 else if (val > attrdef->m_fmax)
496                                                         val = attrdef->m_fmax;
497                                         }
498                                         else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
499                                         {
500                                                 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
501                                                 goto UNDO_AND_ERROR;
502                                         }
503                                         *var = (float)val;
504                                         break;
505                                 }
506                         default:
507                                 // should not happen
508                                 PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name);
509                                 goto UNDO_AND_ERROR;
510                         }
511                 }
512                 // no error, call check function if any
513                 if (attrdef->m_checkFunction != NULL)
514                 {
515                         if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
516                         {
517                                 // post check returned an error, restore values
518                         UNDO_AND_ERROR:
519                                 if (undoBuffer)
520                                 {
521                                         memcpy(sourceBuffer, undoBuffer, bufferSize);
522                                         free(undoBuffer);
523                                 }
524                                 return 1;
525                         }
526                 }
527                 if (undoBuffer)
528                         free(undoBuffer);
529                 return 0;
530         }
531         else    // simple attribute value
532         {
533                 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
534                 {
535                         if (attrdef->m_setFunction == NULL)
536                         {
537                                 PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name);
538                                 return 1;
539                         }
540                         return (*attrdef->m_setFunction)(self, attrdef, value);
541                 }
542                 if (attrdef->m_checkFunction != NULL)
543                 {
544                         // post check function is provided, prepare undo buffer
545                         sourceBuffer = ptr;
546                         switch (attrdef->m_type) 
547                         {
548                         case KX_PYATTRIBUTE_TYPE_BOOL:
549                                 bufferSize = sizeof(bool);
550                                 break;
551                         case KX_PYATTRIBUTE_TYPE_SHORT:
552                                 bufferSize = sizeof(short);
553                                 break;
554                         case KX_PYATTRIBUTE_TYPE_ENUM:
555                         case KX_PYATTRIBUTE_TYPE_INT:
556                                 bufferSize = sizeof(int);
557                                 break;
558                         case KX_PYATTRIBUTE_TYPE_FLOAT:
559                                 bufferSize = sizeof(float);
560                                 break;
561                         case KX_PYATTRIBUTE_TYPE_STRING:
562                                 sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr();
563                                 if (sourceBuffer)
564                                         bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1;
565                                 break;
566                         default:
567                                 PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
568                                 return 1;
569                         }
570                         if (bufferSize)
571                         {
572                                 undoBuffer = malloc(bufferSize);
573                                 if (undoBuffer)
574                                 {
575                                         memcpy(undoBuffer, sourceBuffer, bufferSize);
576                                 }
577                         }
578                 }
579                         
580                 switch (attrdef->m_type) 
581                 {
582                 case KX_PYATTRIBUTE_TYPE_BOOL:
583                         {
584                                 bool *var = reinterpret_cast<bool*>(ptr);
585                                 if (PyInt_Check(value)) 
586                                 {
587                                         *var = (PyInt_AsLong(value) != 0);
588                                 } 
589                                 else if (PyBool_Check(value))
590                                 {
591                                         *var = (value == Py_True);
592                                 }
593                                 else
594                                 {
595                                         PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
596                                         goto FREE_AND_ERROR;
597                                 }
598                                 break;
599                         }
600                 case KX_PYATTRIBUTE_TYPE_SHORT:
601                         {
602                                 short int *var = reinterpret_cast<short int*>(ptr);
603                                 if (PyInt_Check(value)) 
604                                 {
605                                         long val = PyInt_AsLong(value);
606                                         if (attrdef->m_clamp)
607                                         {
608                                                 if (val < attrdef->m_imin)
609                                                         val = attrdef->m_imin;
610                                                 else if (val > attrdef->m_imax)
611                                                         val = attrdef->m_imax;
612                                         }
613                                         else if (val < attrdef->m_imin || val > attrdef->m_imax)
614                                         {
615                                                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
616                                                 goto FREE_AND_ERROR;
617                                         }
618                                         *var = (short int)val;
619                                 }
620                                 else
621                                 {
622                                         PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
623                                         goto FREE_AND_ERROR;
624                                 }
625                                 break;
626                         }
627                 case KX_PYATTRIBUTE_TYPE_ENUM:
628                         // enum are equivalent to int, just make sure that the field size matches:
629                         if (sizeof(int) != attrdef->m_size)
630                         {
631                                 PyErr_Format(PyExc_AttributeError, "attribute size check error for attribute \"%s\", report to blender.org", attrdef->m_name);
632                                 goto FREE_AND_ERROR;
633                         }
634                         // walkthrough
635                 case KX_PYATTRIBUTE_TYPE_INT:
636                         {
637                                 int *var = reinterpret_cast<int*>(ptr);
638                                 if (PyInt_Check(value)) 
639                                 {
640                                         long val = PyInt_AsLong(value);
641                                         if (attrdef->m_clamp)
642                                         {
643                                                 if (val < attrdef->m_imin)
644                                                         val = attrdef->m_imin;
645                                                 else if (val > attrdef->m_imax)
646                                                         val = attrdef->m_imax;
647                                         }
648                                         else if (val < attrdef->m_imin || val > attrdef->m_imax)
649                                         {
650                                                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
651                                                 goto FREE_AND_ERROR;
652                                         }
653                                         *var = (int)val;
654                                 }
655                                 else
656                                 {
657                                         PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
658                                         goto FREE_AND_ERROR;
659                                 }
660                                 break;
661                         }
662                 case KX_PYATTRIBUTE_TYPE_FLOAT:
663                         {
664                                 float *var = reinterpret_cast<float*>(ptr);
665                                 double val = PyFloat_AsDouble(value);
666                                 if (val == -1.0 && PyErr_Occurred())
667                                 {
668                                         PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
669                                         goto FREE_AND_ERROR;
670                                 }
671                                 else if (attrdef->m_clamp)
672                                 {
673                                         if (val < attrdef->m_fmin)
674                                                 val = attrdef->m_fmin;
675                                         else if (val > attrdef->m_fmax)
676                                                 val = attrdef->m_fmax;
677                                 }
678                                 else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
679                                 {
680                                         PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
681                                         goto FREE_AND_ERROR;
682                                 }
683                                 *var = (float)val;
684                                 break;
685                         }
686                 case KX_PYATTRIBUTE_TYPE_STRING:
687                         {
688                                 STR_String *var = reinterpret_cast<STR_String*>(ptr);
689                                 if (PyString_Check(value)) 
690                                 {
691                                         char *val = PyString_AsString(value);
692                                         if (attrdef->m_clamp)
693                                         {
694                                                 if (strlen(val) < attrdef->m_imin)
695                                                 {
696                                                         // can't increase the length of the string
697                                                         PyErr_Format(PyExc_ValueError, "string length too short for attribute \"%s\"", attrdef->m_name);
698                                                         goto FREE_AND_ERROR;
699                                                 }
700                                                 else if (strlen(val) > attrdef->m_imax)
701                                                 {
702                                                         // trim the string
703                                                         char c = val[attrdef->m_imax];
704                                                         val[attrdef->m_imax] = 0;
705                                                         *var = val;
706                                                         val[attrdef->m_imax] = c;
707                                                         break;
708                                                 }
709                                         } else if (strlen(val) < attrdef->m_imin || strlen(val) > attrdef->m_imax)
710                                         {
711                                                 PyErr_Format(PyExc_ValueError, "string length out of range for attribute \"%s\"", attrdef->m_name);
712                                                 goto FREE_AND_ERROR;
713                                         }
714                                         *var = val;
715                                 }
716                                 else
717                                 {
718                                         PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name);
719                                         goto FREE_AND_ERROR;
720                                 }
721                                 break;
722                         }
723                 default:
724                         // should not happen
725                         PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
726                         goto FREE_AND_ERROR;
727                 }
728         }
729         // check if post processing is needed
730         if (attrdef->m_checkFunction != NULL)
731         {
732                 if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
733                 {
734                         // restore value
735                 RESTORE_AND_ERROR:
736                         if (undoBuffer)
737                         {
738                                 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING)
739                                 {
740                                         // special case for STR_String: restore the string
741                                         STR_String *var = reinterpret_cast<STR_String*>(ptr);
742                                         *var = reinterpret_cast<char*>(undoBuffer);
743                                 }
744                                 else
745                                 {
746                                         // other field type have direct values
747                                         memcpy(ptr, undoBuffer, bufferSize);
748                                 }
749                         }
750                 FREE_AND_ERROR:
751                         if (undoBuffer)
752                                 free(undoBuffer);
753                         return 1;
754                 }
755         }
756         if (undoBuffer)
757                 free(undoBuffer);
758         return 0;       
759 }
760
761
762
763 /*------------------------------
764  * PyObjectPlus repr            -- representations
765 ------------------------------*/
766 PyObject *PyObjectPlus::py_repr(void)
767 {
768         PyErr_SetString(PyExc_SystemError, "Representation not overridden by object.");  
769         return NULL;
770 }
771
772 /*------------------------------
773  * PyObjectPlus isA             -- the isA functions
774 ------------------------------*/
775 bool PyObjectPlus::isA(PyTypeObject *T)         // if called with a Type, use "typename"
776 {
777         int i;
778         PyParentObject  P;
779         PyParentObject *Ps = GetParents();
780
781         for (P = Ps[i=0]; P != NULL; P = Ps[i++])
782                 if (P==T)
783                         return true;
784
785         return false;
786 }
787
788
789 bool PyObjectPlus::isA(const char *mytypename)          // check typename of each parent
790 {
791         int i;
792         PyParentObject  P;
793         PyParentObject *Ps = GetParents();
794   
795         for (P = Ps[i=0]; P != NULL; P = Ps[i++])
796                 if (strcmp(P->tp_name, mytypename)==0)
797                         return true;
798
799         return false;
800 }
801
802 PyObject *PyObjectPlus::PyisA(PyObject *value)          // Python wrapper for isA
803 {
804         if (PyType_Check(value)) {
805                 return PyBool_FromLong(isA((PyTypeObject *)value));
806         } else if (PyString_Check(value)) {
807                 return PyBool_FromLong(isA(PyString_AsString(value)));
808         }
809     PyErr_SetString(PyExc_TypeError, "object.isA(value): expected a type or a string");
810     return NULL;        
811 }
812
813
814 void PyObjectPlus::ProcessReplica()
815 {
816         /* Clear the proxy, will be created again if needed with GetProxy()
817          * otherwise the PyObject will point to the wrong reference */
818         m_proxy= NULL;
819 }
820
821 /* Utility function called by the macro py_getattro_up()
822  * for getting ob.__dict__() values from our PyObject
823  * this is used by python for doing dir() on an object, so its good
824  * if we return a list of attributes and methods.
825  * 
826  * Other then making dir() useful the value returned from __dict__() is not useful
827  * since every value is a Py_None
828  * */
829 PyObject *py_getattr_dict(PyObject *pydict, PyObject *tp_dict)
830 {
831     if(pydict==NULL) { /* incase calling __dict__ on the parent of this object raised an error */
832         PyErr_Clear();
833         pydict = PyDict_New();
834     }
835         
836         PyDict_Update(pydict, tp_dict);
837         return pydict;
838 }
839
840
841
842 PyObject *PyObjectPlus::GetProxy_Ext(PyObjectPlus *self, PyTypeObject *tp)
843 {
844         if (self->m_proxy==NULL)
845         {
846                 self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
847                 BGE_PROXY_PYOWNS(self->m_proxy) = false;
848         }
849         //PyObject_Print(self->m_proxy, stdout, 0);
850         //printf("ref %d\n", self->m_proxy->ob_refcnt);
851         
852         BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */
853         Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */
854         return self->m_proxy;
855 }
856
857 PyObject *PyObjectPlus::NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool py_owns)
858 {
859         if (self->m_proxy)
860         {
861                 if(py_owns)
862                 {       /* Free */
863                         BGE_PROXY_REF(self->m_proxy) = NULL;
864                         Py_DECREF(self->m_proxy);
865                         self->m_proxy= NULL;
866                 }
867                 else {
868                         Py_INCREF(self->m_proxy);
869                         return self->m_proxy;
870                 }
871                 
872         }
873         
874         GetProxy_Ext(self, tp);
875         if(py_owns) {
876                 BGE_PROXY_PYOWNS(self->m_proxy) = py_owns;
877                 Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */
878         }
879         return self->m_proxy;
880 }
881
882 #endif //NO_EXP_PYTHON_EMBEDDING
883