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