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