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