4526f8f649bf5395989784030bb525f6595a53ef
[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 PyAttributeDef PyObjectPlus::Attributes[] = {
102         {NULL} //Sentinel
103 };
104
105 /*------------------------------
106  * PyObjectPlus Parents         -- Every class, even the abstract one should have parents
107 ------------------------------*/
108 PyParentObject PyObjectPlus::Parents[] = {&PyObjectPlus::Type, NULL};
109
110 /*------------------------------
111  * PyObjectPlus attributes      -- attributes
112 ------------------------------*/
113 PyObject *PyObjectPlus::py_getattro(PyObject* attr)
114 {
115         PyObject *descr = PyDict_GetItem(Type.tp_dict, attr); \
116         if (descr == NULL) {
117                 if (strcmp(PyString_AsString(attr), "__dict__")==0) {
118                         return py_getattr_dict(NULL, Type.tp_dict); /* no Attributes yet */
119                 }
120                 PyErr_SetString(PyExc_AttributeError, "attribute not found");
121                 return NULL;
122         } else {
123                 return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, (PyObject *)this); \
124         }
125   //if (streq(attr, "type"))
126   //  return Py_BuildValue("s", (*(GetParents()))->tp_name);
127 }
128
129 int PyObjectPlus::py_delattro(PyObject* attr)
130 {
131         PyErr_SetString(PyExc_AttributeError, "attribute cant be deleted");
132         return 1;
133 }
134
135 int PyObjectPlus::py_setattro(PyObject *attr, PyObject* value)
136 {
137         //return PyObject::py_setattro(attr,value);
138         //cerr << "Unknown attribute" << endl;
139         PyErr_SetString(PyExc_AttributeError, "attribute cant be set");
140         return 1;
141 }
142
143 PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef)
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 #if 0
250 PyObject *PyObjectPlus::py_getattro_self(const PyAttributeDef attrlist[], void *self, PyObject *attr)
251 {
252         char *attr_str= PyString_AsString(attr);
253         const PyAttributeDef *attrdef;
254         
255         for (attrdef=attrlist; attrdef->m_name != NULL; attrdef++)
256                 if (!strcmp(attr_str, attrdef->m_name))
257                         return py_get_attrdef(self, attrdef);
258         
259         return NULL;
260 }
261 #endif
262
263 int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyObject *value)
264 {
265         void *undoBuffer = NULL;
266         void *sourceBuffer = NULL;
267         size_t bufferSize = 0;
268         
269         char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
270         if (attrdef->m_length > 1)
271         {
272                 if (!PySequence_Check(value)) 
273                 {
274                         PyErr_SetString(PyExc_TypeError, "expected a sequence");
275                         return 1;
276                 }
277                 if (PySequence_Size(value) != attrdef->m_length)
278                 {
279                         PyErr_SetString(PyExc_TypeError, "incorrect number of elements in sequence");
280                         return 1;
281                 }
282                 switch (attrdef->m_type) 
283                 {
284                 case KX_PYATTRIBUTE_TYPE_FUNCTION:
285                         if (attrdef->m_setFunction == NULL) 
286                         {
287                                 PyErr_SetString(PyExc_AttributeError, "function attribute without function, report to blender.org");
288                                 return 1;
289                         }
290                         return (*attrdef->m_setFunction)(self, attrdef, value);
291                 case KX_PYATTRIBUTE_TYPE_BOOL:
292                         bufferSize = sizeof(bool);
293                         break;
294                 case KX_PYATTRIBUTE_TYPE_SHORT:
295                         bufferSize = sizeof(short int);
296                         break;
297                 case KX_PYATTRIBUTE_TYPE_ENUM:
298                 case KX_PYATTRIBUTE_TYPE_INT:
299                         bufferSize = sizeof(int);
300                         break;
301                 case KX_PYATTRIBUTE_TYPE_FLOAT:
302                         bufferSize = sizeof(float);
303                         break;
304                 default:
305                         // should not happen
306                         PyErr_SetString(PyExc_AttributeError, "Unsupported attribute type, report to blender.org");
307                         return 1;
308                 }
309                 // let's implement a smart undo method
310                 bufferSize *= attrdef->m_length;
311                 undoBuffer = malloc(bufferSize);
312                 sourceBuffer = ptr;
313                 if (undoBuffer)
314                 {
315                         memcpy(undoBuffer, sourceBuffer, bufferSize);
316                 }
317                 for (int i=0; i<attrdef->m_length; i++)
318                 {
319                         PyObject *item = PySequence_GetItem(value, i); /* new ref */
320                         // we can decrement the reference immediately, the reference count
321                         // is at least 1 because the item is part of an array
322                         Py_DECREF(item);
323                         switch (attrdef->m_type) 
324                         {
325                         case KX_PYATTRIBUTE_TYPE_BOOL:
326                                 {
327                                         bool *var = reinterpret_cast<bool*>(ptr);
328                                         ptr += sizeof(bool);
329                                         if (PyInt_Check(item)) 
330                                         {
331                                                 *var = (PyInt_AsLong(item) != 0);
332                                         } 
333                                         else if (PyBool_Check(item))
334                                         {
335                                                 *var = (item == Py_True);
336                                         }
337                                         else
338                                         {
339                                                 PyErr_SetString(PyExc_TypeError, "expected an integer or a bool");
340                                                 goto UNDO_AND_ERROR;
341                                         }
342                                         break;
343                                 }
344                         case KX_PYATTRIBUTE_TYPE_SHORT:
345                                 {
346                                         short int *var = reinterpret_cast<short int*>(ptr);
347                                         ptr += sizeof(short int);
348                                         if (PyInt_Check(item)) 
349                                         {
350                                                 long val = PyInt_AsLong(item);
351                                                 if (attrdef->m_clamp)
352                                                 {
353                                                         if (val < attrdef->m_imin)
354                                                                 val = attrdef->m_imin;
355                                                         else if (val > attrdef->m_imax)
356                                                                 val = attrdef->m_imax;
357                                                 }
358                                                 else if (val < attrdef->m_imin || val > attrdef->m_imax)
359                                                 {
360                                                         PyErr_SetString(PyExc_ValueError, "item value out of range");
361                                                         goto UNDO_AND_ERROR;
362                                                 }
363                                                 *var = (short int)val;
364                                         }
365                                         else
366                                         {
367                                                 PyErr_SetString(PyExc_TypeError, "expected an integer");
368                                                 goto UNDO_AND_ERROR;
369                                         }
370                                         break;
371                                 }
372                         case KX_PYATTRIBUTE_TYPE_ENUM:
373                                 // enum are equivalent to int, just make sure that the field size matches:
374                                 if (sizeof(int) != attrdef->m_size)
375                                 {
376                                         PyErr_SetString(PyExc_AttributeError, "attribute size check error, report to blender.org");
377                                         goto UNDO_AND_ERROR;
378                                 }
379                                 // walkthrough
380                         case KX_PYATTRIBUTE_TYPE_INT:
381                                 {
382                                         int *var = reinterpret_cast<int*>(ptr);
383                                         ptr += sizeof(int);
384                                         if (PyInt_Check(item)) 
385                                         {
386                                                 long val = PyInt_AsLong(item);
387                                                 if (attrdef->m_clamp)
388                                                 {
389                                                         if (val < attrdef->m_imin)
390                                                                 val = attrdef->m_imin;
391                                                         else if (val > attrdef->m_imax)
392                                                                 val = attrdef->m_imax;
393                                                 }
394                                                 else if (val < attrdef->m_imin || val > attrdef->m_imax)
395                                                 {
396                                                         PyErr_SetString(PyExc_ValueError, "item value out of range");
397                                                         goto UNDO_AND_ERROR;
398                                                 }
399                                                 *var = (int)val;
400                                         }
401                                         else
402                                         {
403                                                 PyErr_SetString(PyExc_TypeError, "expected an integer");
404                                                 goto UNDO_AND_ERROR;
405                                         }
406                                         break;
407                                 }
408                         case KX_PYATTRIBUTE_TYPE_FLOAT:
409                                 {
410                                         float *var = reinterpret_cast<float*>(ptr);
411                                         ptr += sizeof(float);
412                                         double val = PyFloat_AsDouble(item);
413                                         if (val == -1.0 && PyErr_Occurred())
414                                         {
415                                                 PyErr_SetString(PyExc_TypeError, "expected a float");
416                                                 goto UNDO_AND_ERROR;
417                                         }
418                                         else if (attrdef->m_clamp) 
419                                         {
420                                                 if (val < attrdef->m_fmin)
421                                                         val = attrdef->m_fmin;
422                                                 else if (val > attrdef->m_fmax)
423                                                         val = attrdef->m_fmax;
424                                         }
425                                         else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
426                                         {
427                                                 PyErr_SetString(PyExc_ValueError, "item value out of range");
428                                                 goto UNDO_AND_ERROR;
429                                         }
430                                         *var = (float)val;
431                                         break;
432                                 }
433                         default:
434                                 // should not happen
435                                 PyErr_SetString(PyExc_AttributeError, "attribute type check error, report to blender.org");
436                                 goto UNDO_AND_ERROR;
437                         }
438                 }
439                 // no error, call check function if any
440                 if (attrdef->m_checkFunction != NULL)
441                 {
442                         if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
443                         {
444                                 // post check returned an error, restore values
445                         UNDO_AND_ERROR:
446                                 if (undoBuffer)
447                                 {
448                                         memcpy(sourceBuffer, undoBuffer, bufferSize);
449                                         free(undoBuffer);
450                                 }
451                                 return 1;
452                         }
453                 }
454                 if (undoBuffer)
455                         free(undoBuffer);
456                 return 0;
457         }
458         else    // simple attribute value
459         {
460                 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
461                 {
462                         if (attrdef->m_setFunction == NULL)
463                         {
464                                 PyErr_SetString(PyExc_AttributeError, "function attribute without function, report to blender.org");
465                                 return 1;
466                         }
467                         return (*attrdef->m_setFunction)(self, attrdef, value);
468                 }
469                 if (attrdef->m_checkFunction != NULL)
470                 {
471                         // post check function is provided, prepare undo buffer
472                         sourceBuffer = ptr;
473                         switch (attrdef->m_type) 
474                         {
475                         case KX_PYATTRIBUTE_TYPE_BOOL:
476                                 bufferSize = sizeof(bool);
477                                 break;
478                         case KX_PYATTRIBUTE_TYPE_SHORT:
479                                 bufferSize = sizeof(short);
480                                 break;
481                         case KX_PYATTRIBUTE_TYPE_ENUM:
482                         case KX_PYATTRIBUTE_TYPE_INT:
483                                 bufferSize = sizeof(int);
484                                 break;
485                         case KX_PYATTRIBUTE_TYPE_FLOAT:
486                                 bufferSize = sizeof(float);
487                                 break;
488                         case KX_PYATTRIBUTE_TYPE_STRING:
489                                 sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr();
490                                 if (sourceBuffer)
491                                         bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1;
492                                 break;
493                         default:
494                                 PyErr_SetString(PyExc_AttributeError, "unknown attribute type, report to blender.org");
495                                 return 1;
496                         }
497                         if (bufferSize)
498                         {
499                                 undoBuffer = malloc(bufferSize);
500                                 if (undoBuffer)
501                                 {
502                                         memcpy(undoBuffer, sourceBuffer, bufferSize);
503                                 }
504                         }
505                 }
506                         
507                 switch (attrdef->m_type) 
508                 {
509                 case KX_PYATTRIBUTE_TYPE_BOOL:
510                         {
511                                 bool *var = reinterpret_cast<bool*>(ptr);
512                                 if (PyInt_Check(value)) 
513                                 {
514                                         *var = (PyInt_AsLong(value) != 0);
515                                 } 
516                                 else if (PyBool_Check(value))
517                                 {
518                                         *var = (value == Py_True);
519                                 }
520                                 else
521                                 {
522                                         PyErr_SetString(PyExc_TypeError, "expected an integer or a bool");
523                                         goto FREE_AND_ERROR;
524                                 }
525                                 break;
526                         }
527                 case KX_PYATTRIBUTE_TYPE_SHORT:
528                         {
529                                 short int *var = reinterpret_cast<short int*>(ptr);
530                                 if (PyInt_Check(value)) 
531                                 {
532                                         long val = PyInt_AsLong(value);
533                                         if (attrdef->m_clamp)
534                                         {
535                                                 if (val < attrdef->m_imin)
536                                                         val = attrdef->m_imin;
537                                                 else if (val > attrdef->m_imax)
538                                                         val = attrdef->m_imax;
539                                         }
540                                         else if (val < attrdef->m_imin || val > attrdef->m_imax)
541                                         {
542                                                 PyErr_SetString(PyExc_ValueError, "value out of range");
543                                                 goto FREE_AND_ERROR;
544                                         }
545                                         *var = (short int)val;
546                                 }
547                                 else
548                                 {
549                                         PyErr_SetString(PyExc_TypeError, "expected an integer");
550                                         goto FREE_AND_ERROR;
551                                 }
552                                 break;
553                         }
554                 case KX_PYATTRIBUTE_TYPE_ENUM:
555                         // enum are equivalent to int, just make sure that the field size matches:
556                         if (sizeof(int) != attrdef->m_size)
557                         {
558                                 PyErr_SetString(PyExc_AttributeError, "attribute size check error, report to blender.org");
559                                 goto FREE_AND_ERROR;
560                         }
561                         // walkthrough
562                 case KX_PYATTRIBUTE_TYPE_INT:
563                         {
564                                 int *var = reinterpret_cast<int*>(ptr);
565                                 if (PyInt_Check(value)) 
566                                 {
567                                         long val = PyInt_AsLong(value);
568                                         if (attrdef->m_clamp)
569                                         {
570                                                 if (val < attrdef->m_imin)
571                                                         val = attrdef->m_imin;
572                                                 else if (val > attrdef->m_imax)
573                                                         val = attrdef->m_imax;
574                                         }
575                                         else if (val < attrdef->m_imin || val > attrdef->m_imax)
576                                         {
577                                                 PyErr_SetString(PyExc_ValueError, "value out of range");
578                                                 goto FREE_AND_ERROR;
579                                         }
580                                         *var = (int)val;
581                                 }
582                                 else
583                                 {
584                                         PyErr_SetString(PyExc_TypeError, "expected an integer");
585                                         goto FREE_AND_ERROR;
586                                 }
587                                 break;
588                         }
589                 case KX_PYATTRIBUTE_TYPE_FLOAT:
590                         {
591                                 float *var = reinterpret_cast<float*>(ptr);
592                                 double val = PyFloat_AsDouble(value);
593                                 if (val == -1.0 && PyErr_Occurred())
594                                 {
595                                         PyErr_SetString(PyExc_TypeError, "expected a float");
596                                         goto FREE_AND_ERROR;
597                                 }
598                                 else if (attrdef->m_clamp)
599                                 {
600                                         if (val < attrdef->m_fmin)
601                                                 val = attrdef->m_fmin;
602                                         else if (val > attrdef->m_fmax)
603                                                 val = attrdef->m_fmax;
604                                 }
605                                 else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
606                                 {
607                                         PyErr_SetString(PyExc_ValueError, "value out of range");
608                                         goto FREE_AND_ERROR;
609                                 }
610                                 *var = (float)val;
611                                 break;
612                         }
613                 case KX_PYATTRIBUTE_TYPE_STRING:
614                         {
615                                 STR_String *var = reinterpret_cast<STR_String*>(ptr);
616                                 if (PyString_Check(value)) 
617                                 {
618                                         char *val = PyString_AsString(value);
619                                         if (attrdef->m_clamp)
620                                         {
621                                                 if (strlen(val) < attrdef->m_imin)
622                                                 {
623                                                         // can't increase the length of the string
624                                                         PyErr_SetString(PyExc_ValueError, "string length too short");
625                                                         goto FREE_AND_ERROR;
626                                                 }
627                                                 else if (strlen(val) > attrdef->m_imax)
628                                                 {
629                                                         // trim the string
630                                                         char c = val[attrdef->m_imax];
631                                                         val[attrdef->m_imax] = 0;
632                                                         *var = val;
633                                                         val[attrdef->m_imax] = c;
634                                                         break;
635                                                 }
636                                         } else if (strlen(val) < attrdef->m_imin || strlen(val) > attrdef->m_imax)
637                                         {
638                                                 PyErr_SetString(PyExc_ValueError, "string length out of range");
639                                                 goto FREE_AND_ERROR;
640                                         }
641                                         *var = val;
642                                 }
643                                 else
644                                 {
645                                         PyErr_SetString(PyExc_TypeError, "expected a string");
646                                         goto FREE_AND_ERROR;
647                                 }
648                                 break;
649                         }
650                 default:
651                         // should not happen
652                         PyErr_SetString(PyExc_AttributeError, "unknown attribute type, report to blender.org");
653                         goto FREE_AND_ERROR;
654                 }
655         }
656         // check if post processing is needed
657         if (attrdef->m_checkFunction != NULL)
658         {
659                 if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
660                 {
661                         // restore value
662                 RESTORE_AND_ERROR:
663                         if (undoBuffer)
664                         {
665                                 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING)
666                                 {
667                                         // special case for STR_String: restore the string
668                                         STR_String *var = reinterpret_cast<STR_String*>(ptr);
669                                         *var = reinterpret_cast<char*>(undoBuffer);
670                                 }
671                                 else
672                                 {
673                                         // other field type have direct values
674                                         memcpy(ptr, undoBuffer, bufferSize);
675                                 }
676                         }
677                 FREE_AND_ERROR:
678                         if (undoBuffer)
679                                 free(undoBuffer);
680                         return 1;
681                 }
682         }
683         if (undoBuffer)
684                 free(undoBuffer);
685         return 0;       
686 }
687
688 #if 0
689 int PyObjectPlus::py_setattro_self(const PyAttributeDef attrlist[], void *self, PyObject *attr, PyObject *value)
690 {
691         const PyAttributeDef *attrdef;
692         char *attr_str= PyString_AsString(attr);
693
694         for (attrdef=attrlist; attrdef->m_name != NULL; attrdef++)
695         {
696                 if (!strcmp(attr_str, attrdef->m_name))
697                 {
698                         if (attrdef->m_access == KX_PYATTRIBUTE_RO ||
699                                 attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
700                         {
701                                 PyErr_SetString(PyExc_AttributeError, "property is read-only");
702                                 return 1;
703                         }
704                         
705                         return py_set_attrdef(self, attrdef, value);
706                 }
707         }
708         return -1;                      
709 }
710 #endif
711
712 /*------------------------------
713  * PyObjectPlus repr            -- representations
714 ------------------------------*/
715 PyObject *PyObjectPlus::py_repr(void)
716 {
717         PyErr_SetString(PyExc_SystemError, "Representation not overridden by object.");  
718         return NULL;
719 }
720
721 /*------------------------------
722  * PyObjectPlus isA             -- the isA functions
723 ------------------------------*/
724 bool PyObjectPlus::isA(PyTypeObject *T)         // if called with a Type, use "typename"
725 {
726         int i;
727         PyParentObject  P;
728         PyParentObject *Ps = GetParents();
729
730         for (P = Ps[i=0]; P != NULL; P = Ps[i++])
731                 if (P==T)
732                         return true;
733
734         return false;
735 }
736
737
738 bool PyObjectPlus::isA(const char *mytypename)          // check typename of each parent
739 {
740         int i;
741         PyParentObject  P;
742         PyParentObject *Ps = GetParents();
743   
744         for (P = Ps[i=0]; P != NULL; P = Ps[i++])
745                 if (strcmp(P->tp_name, mytypename)==0)
746                         return true;
747
748         return false;
749 }
750
751 PyObject *PyObjectPlus::Py_isA(PyObject *value)         // Python wrapper for isA
752 {
753         if (PyType_Check(value)) {
754                 return PyBool_FromLong(isA((PyTypeObject *)value));
755         } else if (PyString_Check(value)) {
756                 return PyBool_FromLong(isA(PyString_AsString(value)));
757         }
758     PyErr_SetString(PyExc_TypeError, "expected a type or a string");
759     return NULL;        
760 }
761
762 /* Utility function called by the macro py_getattro_up()
763  * for getting ob.__dict__() values from our PyObject
764  * this is used by python for doing dir() on an object, so its good
765  * if we return a list of attributes and methods.
766  * 
767  * Other then making dir() useful the value returned from __dict__() is not useful
768  * since every value is a Py_None
769  * */
770 PyObject *py_getattr_dict(PyObject *pydict, PyObject *tp_dict)
771 {
772     if(pydict==NULL) { /* incase calling __dict__ on the parent of this object raised an error */
773         PyErr_Clear();
774         pydict = PyDict_New();
775     }
776         
777         PyDict_Update(pydict, tp_dict);
778         return pydict;
779 }
780
781 #endif //NO_EXP_PYTHON_EMBEDDING
782