doxygen: gameengine/Expressions tagged.
[blender-staging.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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 /** \file gameengine/Expressions/PyObjectPlus.cpp
30  *  \ingroup expressions
31  */
32
33
34 /*------------------------------
35  * PyObjectPlus cpp
36  *
37  * C++ library routines for Crawl 3.2
38  *
39  * Derived from work by
40  * David Redish
41  * graduate student
42  * Computer Science Department 
43  * Carnegie Mellon University (CMU)
44  * Center for the Neural Basis of Cognition (CNBC) 
45  * http://www.python.org/doc/PyCPP.html
46  *
47 ------------------------------*/
48 #include <MT_assert.h>
49 #include "stdlib.h"
50 #include "PyObjectPlus.h"
51 #include "STR_String.h"
52 #include "MT_Vector3.h"
53 #include "MEM_guardedalloc.h"
54
55 PyObjectPlus::~PyObjectPlus()
56 {
57 #ifdef WITH_PYTHON
58         if(m_proxy) {
59                 BGE_PROXY_REF(m_proxy)= NULL;
60                 Py_DECREF(m_proxy);                     /* Remove own reference, python may still have 1 */
61         }
62 //      assert(ob_refcnt==0);
63 #endif
64 }
65
66 PyObjectPlus::PyObjectPlus() : SG_QList()                               // constructor
67 {
68 #ifdef WITH_PYTHON
69         m_proxy= NULL;
70 #endif
71 };
72
73 void PyObjectPlus::ProcessReplica()
74 {
75 #ifdef WITH_PYTHON
76         /* Clear the proxy, will be created again if needed with GetProxy()
77          * otherwise the PyObject will point to the wrong reference */
78         m_proxy= NULL;
79 #endif
80 }
81
82 /* Sometimes we might want to manually invalidate a BGE type even if
83  * it hasnt been released by the BGE, say for example when an object
84  * is removed from a scene, accessing it may cause problems.
85  *
86  * In this case the current proxy is made invalid, disowned,
87  * and will raise an error on access. However if python can get access
88  * to this class again it will make a new proxy and work as expected.
89  */
90 void PyObjectPlus::InvalidateProxy()            // check typename of each parent
91 {
92 #ifdef WITH_PYTHON
93         if(m_proxy) {
94                 BGE_PROXY_REF(m_proxy)=NULL;
95                 Py_DECREF(m_proxy);
96                 m_proxy= NULL;
97         }
98 #endif
99 }
100
101
102 #ifdef WITH_PYTHON
103
104 /*------------------------------
105  * PyObjectPlus Type            -- Every class, even the abstract one should have a Type
106 ------------------------------*/
107
108
109 PyTypeObject PyObjectPlus::Type = {
110         PyVarObject_HEAD_INIT(NULL, 0)
111         "PyObjectPlus",                 /*tp_name*/
112         sizeof(PyObjectPlus_Proxy),             /*tp_basicsize*/
113         0,                              /*tp_itemsize*/
114         /* methods */
115         py_base_dealloc,
116         0,
117         0,
118         0,
119         0,
120         py_base_repr,
121         0,0,0,0,0,0,0,0,0,
122         Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
123         0,0,0,0,0,0,0,
124         Methods,
125         0,
126         0,
127         NULL // no subtype
128 };
129
130 PyObject *PyObjectPlus::py_base_repr(PyObject *self)                    // This should be the entry in Type.
131 {
132         PyObjectPlus *self_plus= BGE_PROXY_REF(self);
133         if(self_plus==NULL) {
134                 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
135                 return NULL;
136         }
137         return self_plus->py_repr();  
138 }
139
140
141 PyObject * PyObjectPlus::py_base_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
142 {
143         PyTypeObject *base_type;
144         PyObjectPlus_Proxy *base = NULL;
145
146         if (!PyArg_ParseTuple(args, "O:Base PyObjectPlus", &base))
147                 return NULL;
148
149         /* the 'base' PyObject may be subclassed (multiple times even)
150          * we need to find the first C++ defined class to check 'type'
151          * is a subclass of the base arguments type.
152          *
153          * This way we can share one tp_new function for every PyObjectPlus
154          *
155          * eg.
156          *
157          * # CustomOb is called 'type' in this C code
158          * class CustomOb(GameTypes.KX_GameObject):
159          *     pass
160          *
161          * # this calls py_base_new(...), the type of 'CustomOb' is checked to be a subclass of the 'cont.owner' type
162          * ob = CustomOb(cont.owner)
163          *
164          * */
165         base_type= Py_TYPE(base);
166         while(base_type && !BGE_PROXY_CHECK_TYPE(base_type))
167                 base_type= base_type->tp_base;
168
169         if(base_type==NULL || !BGE_PROXY_CHECK_TYPE(base_type)) {
170                 PyErr_SetString(PyExc_TypeError, "can't subclass from a blender game type because the argument given is not a game class or subclass");
171                 return NULL;
172         }
173
174         /* use base_type rather then Py_TYPE(base) because we could already be subtyped */
175         if(!PyType_IsSubtype(type, base_type)) {
176                 PyErr_Format(PyExc_TypeError, "can't subclass blender game type <%s> from <%s> because it is not a subclass", base_type->tp_name, type->tp_name);
177                 return NULL;
178         }
179
180         /* invalidate the existing base and return a new subclassed one,
181          * this is a bit dodgy in that it also attaches its self to the existing object
182          * which is not really 'correct' python OO but for our use its OK. */
183
184         PyObjectPlus_Proxy *ret = (PyObjectPlus_Proxy *) type->tp_alloc(type, 0); /* starts with 1 ref, used for the return ref' */
185         ret->ref= base->ref;
186         ret->ptr= base->ptr;
187         ret->py_owns= base->py_owns;
188         ret->py_ref = base->py_ref;
189
190         if (ret->py_ref) {
191                 base->ref= NULL;                /* invalidate! disallow further access */
192                 base->ptr = NULL;
193                 if (ret->ref)
194                         ret->ref->m_proxy= NULL;
195                 /* 'base' may be free'd after this func finished but not necessarily
196                  * there is no reference to the BGE data now so it will throw an error on access */
197                 Py_DECREF(base);
198                 if (ret->ref) {
199                         ret->ref->m_proxy= (PyObject *)ret; /* no need to add a ref because one is added when creating. */
200                         Py_INCREF(ret); /* we return a new ref but m_proxy holds a ref so we need to add one */
201                 }
202         } else {
203                 // generic structures don't hold a reference to this proxy, so don't increment ref count
204                 if (ret->py_owns)
205                         // but if the proxy owns the structure, there can be only one owner
206                         base->ptr= NULL;
207         }
208
209         return (PyObject *)ret;
210 }
211
212 void PyObjectPlus::py_base_dealloc(PyObject *self)                              // python wrapper
213 {
214         if (BGE_PROXY_PYREF(self)) {
215                 PyObjectPlus *self_plus= BGE_PROXY_REF(self);
216                 if(self_plus) {
217                         if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it  */
218                                 self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */
219                                 delete self_plus;
220                         }
221                         BGE_PROXY_REF(self)= NULL; // not really needed
222                 }
223                 // the generic pointer is not deleted directly, only through self_plus
224                 BGE_PROXY_PTR(self)= NULL; // not really needed
225         } else {
226                 void *ptr= BGE_PROXY_PTR(self);
227                 if(ptr) {
228                         if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it  */
229                                 // generic structure owned by python MUST be created though MEM_alloc
230                                 MEM_freeN(ptr);
231                         }
232                         BGE_PROXY_PTR(self)= NULL; // not really needed
233                 }
234         }
235 #if 0
236         /* is ok normally but not for subtyping, use tp_free instead. */
237         PyObject_DEL( self );
238 #else
239         Py_TYPE(self)->tp_free(self);
240 #endif
241 };
242
243 /*------------------------------
244  * PyObjectPlus Methods         -- Every class, even the abstract one should have a Methods
245 ------------------------------*/
246 PyMethodDef PyObjectPlus::Methods[] = {
247   {NULL, NULL}          /* Sentinel */
248 };
249
250 #define attr_invalid (&(PyObjectPlus::Attributes[0]))
251 PyAttributeDef PyObjectPlus::Attributes[] = {
252         KX_PYATTRIBUTE_RO_FUNCTION("invalid",           PyObjectPlus, pyattr_get_invalid),
253         {NULL} //Sentinel
254 };
255
256
257
258 PyObject* PyObjectPlus::pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
259 {
260         return PyBool_FromLong(self_v ? 0:1);
261 }
262
263 /* note, this is called as a python 'getset, where the PyAttributeDef is the closure */
264 PyObject *PyObjectPlus::py_get_attrdef(PyObject *self_py, const PyAttributeDef *attrdef)
265 {
266         PyObjectPlus *ref= (BGE_PROXY_REF(self_py));
267         char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref;
268         if(ptr == NULL || (BGE_PROXY_PYREF(self_py) && (ref==NULL || !ref->py_is_valid()))) {
269                 if(attrdef == attr_invalid)
270                         Py_RETURN_TRUE; // dont bother running the function
271
272                 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
273                 return NULL;
274         }
275
276         if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
277         {
278                 // fake attribute, ignore
279                 return NULL;
280         }
281         if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
282         {
283                 // the attribute has no field correspondance, handover processing to function.
284                 if (attrdef->m_getFunction == NULL)
285                         return NULL;
286                 return (*attrdef->m_getFunction)(ptr, attrdef);
287         }
288         ptr += attrdef->m_offset;
289         if (attrdef->m_length > 1)
290         {
291                 PyObject* resultlist = PyList_New(attrdef->m_length);
292                 for (unsigned int i=0; i<attrdef->m_length; i++)
293                 {
294                         switch (attrdef->m_type) {
295                         case KX_PYATTRIBUTE_TYPE_BOOL:
296                                 {
297                                         bool *val = reinterpret_cast<bool*>(ptr);
298                                         ptr += sizeof(bool);
299                                         PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val));
300                                         break;
301                                 }
302                         case KX_PYATTRIBUTE_TYPE_SHORT:
303                                 {
304                                         short int *val = reinterpret_cast<short int*>(ptr);
305                                         ptr += sizeof(short int);
306                                         PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val));
307                                         break;
308                                 }
309                         case KX_PYATTRIBUTE_TYPE_ENUM:
310                                 // enum are like int, just make sure the field size is the same
311                                 if (sizeof(int) != attrdef->m_size)
312                                 {
313                                         Py_DECREF(resultlist);
314                                         return NULL;
315                                 }
316                                 // walkthrough
317                         case KX_PYATTRIBUTE_TYPE_INT:
318                                 {
319                                         int *val = reinterpret_cast<int*>(ptr);
320                                         ptr += sizeof(int);
321                                         PyList_SET_ITEM(resultlist,i,PyLong_FromSsize_t(*val));
322                                         break;
323                                 }
324                         case KX_PYATTRIBUTE_TYPE_FLOAT:
325                                 {
326                                         float *val = reinterpret_cast<float*>(ptr);
327                                         ptr += sizeof(float);
328                                         PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(*val));
329                                         break;
330                                 }
331                         default:
332                                 // no support for array of complex data
333                                 Py_DECREF(resultlist);
334                                 return NULL;
335                         }
336                 }
337                 return resultlist;
338         }
339         else
340         {
341                 switch (attrdef->m_type) {
342                 case KX_PYATTRIBUTE_TYPE_FLAG:
343                         {
344                                 bool bval;
345                                 switch (attrdef->m_size) {
346                                 case 1:
347                                         {
348                                                 unsigned char *val = reinterpret_cast<unsigned char*>(ptr);
349                                                 bval = (*val & attrdef->m_imin);
350                                                 break;
351                                         }
352                                 case 2:
353                                         {
354                                                 unsigned short *val = reinterpret_cast<unsigned short*>(ptr);
355                                                 bval = (*val & attrdef->m_imin);
356                                                 break;
357                                         }
358                                 case 4:
359                                         {
360                                                 unsigned int *val = reinterpret_cast<unsigned int*>(ptr);
361                                                 bval = (*val & attrdef->m_imin);
362                                                 break;
363                                         }
364                                 default:
365                                         return NULL;
366                                 }
367                                 if (attrdef->m_imax)
368                                         bval = !bval;
369                                 return PyLong_FromSsize_t(bval);
370                         }
371                 case KX_PYATTRIBUTE_TYPE_BOOL:
372                         {
373                                 bool *val = reinterpret_cast<bool*>(ptr);
374                                 return PyLong_FromSsize_t(*val);
375                         }
376                 case KX_PYATTRIBUTE_TYPE_SHORT:
377                         {
378                                 short int *val = reinterpret_cast<short int*>(ptr);
379                                 return PyLong_FromSsize_t(*val);
380                         }
381                 case KX_PYATTRIBUTE_TYPE_ENUM:
382                         // enum are like int, just make sure the field size is the same
383                         if (sizeof(int) != attrdef->m_size)
384                         {
385                                 return NULL;
386                         }
387                         // walkthrough
388                 case KX_PYATTRIBUTE_TYPE_INT:
389                         {
390                                 int *val = reinterpret_cast<int*>(ptr);
391                                 return PyLong_FromSsize_t(*val);
392                         }
393                 case KX_PYATTRIBUTE_TYPE_FLOAT:
394                         {
395                                 float *val = reinterpret_cast<float*>(ptr);
396                                 if (attrdef->m_imin == 0) {
397                                         if (attrdef->m_imax == 0) {
398                                                 return PyFloat_FromDouble(*val);
399                                         } else {
400                                                 // vector, verify size
401                                                 if (attrdef->m_size != attrdef->m_imax*sizeof(float)) 
402                                                 {
403                                                         return NULL;
404                                                 }
405 #ifdef USE_MATHUTILS
406                                                 return newVectorObject(val, attrdef->m_imax, Py_NEW, NULL);
407 #else
408                                                 PyObject* resultlist = PyList_New(attrdef->m_imax);
409                                                 for (unsigned int i=0; i<attrdef->m_imax; i++)
410                                                 {
411                                                         PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(val[i]));
412                                                 }
413                                                 return resultlist;
414 #endif
415                                         }
416                                 } else {
417                                         // matrix case
418                                         if (attrdef->m_size != attrdef->m_imax*attrdef->m_imin*sizeof(float)) 
419                                         {
420                                                 return NULL;
421                                         }
422 #ifdef USE_MATHUTILS
423                                         return newMatrixObject(val, attrdef->m_imin, attrdef->m_imax, Py_WRAP, NULL);
424 #else
425                                         PyObject* collist = PyList_New(attrdef->m_imin);
426                                         for (unsigned int i=0; i<attrdef->m_imin; i++)
427                                         {
428                                                 PyObject* col = PyList_New(attrdef->m_imax);
429                                                 for (unsigned int j=0; j<attrdef->m_imax; j++)
430                                                 {
431                                                         PyList_SET_ITEM(col,j,PyFloat_FromDouble(val[j]));
432                                                 }
433                                                 PyList_SET_ITEM(collist,i,col);
434                                                 val += attrdef->m_imax;
435                                         }
436                                         return collist;
437 #endif
438                                 }
439                         }
440                 case KX_PYATTRIBUTE_TYPE_VECTOR:
441                         {
442                                 MT_Vector3 *val = reinterpret_cast<MT_Vector3*>(ptr);
443 #ifdef USE_MATHUTILS
444                                 float fval[3]= {(*val)[0], (*val)[1], (*val)[2]};
445                                 return newVectorObject(fval, 3, Py_NEW, NULL);
446 #else
447                                 PyObject* resultlist = PyList_New(3);
448                                 for (unsigned int i=0; i<3; i++)
449                                 {
450                                         PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble((*val)[i]));
451                                 }
452                                 return resultlist;
453 #endif
454                         }
455                 case KX_PYATTRIBUTE_TYPE_STRING:
456                         {
457                                 STR_String *val = reinterpret_cast<STR_String*>(ptr);
458                                 return PyUnicode_FromString(*val);
459                         }
460                 case KX_PYATTRIBUTE_TYPE_CHAR:
461                         {
462                                 return PyUnicode_FromString(ptr);
463                         }
464                 default:
465                         return NULL;
466                 }
467         }
468 }
469
470
471 static bool py_check_attr_float(float *var, PyObject *value, const PyAttributeDef *attrdef)
472 {
473         double val = PyFloat_AsDouble(value);
474         if (val == -1.0 && PyErr_Occurred())
475         {
476                 PyErr_Format(PyExc_TypeError, "expected float value for attribute \"%s\"", attrdef->m_name);
477                 return false;
478         }
479         if (attrdef->m_clamp)
480         {
481                 if (val < attrdef->m_fmin)
482                         val = attrdef->m_fmin;
483                 else if (val > attrdef->m_fmax)
484                         val = attrdef->m_fmax;
485         }
486         else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
487         {
488                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
489                 return false;
490         }
491         *var = (float)val;
492         return true;
493 }
494
495 /* note, this is called as a python getset */
496 int PyObjectPlus::py_set_attrdef(PyObject *self_py, PyObject *value, const PyAttributeDef *attrdef)
497 {
498         PyObjectPlus *ref= (BGE_PROXY_REF(self_py));
499         char* ptr = (attrdef->m_usePtr) ? (char*)BGE_PROXY_PTR(self_py) : (char*)ref;
500         if(ref==NULL || !ref->py_is_valid() || ptr==NULL) {
501                 PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
502                 return PY_SET_ATTR_FAIL;
503         }
504
505         void *undoBuffer = NULL;
506         void *sourceBuffer = NULL;
507         size_t bufferSize = 0;
508         PyObject *item = NULL;  // to store object that must be dereferenced in case of error
509         PyObject *list = NULL;  // to store object that must be dereferenced in case of error
510         
511         ptr += attrdef->m_offset;
512         if (attrdef->m_length > 1)
513         {
514                 if (!PySequence_Check(value)) 
515                 {
516                         PyErr_Format(PyExc_TypeError, "expected a sequence for attribute \"%s\"", attrdef->m_name);
517                         return PY_SET_ATTR_FAIL;
518                 }
519                 if (PySequence_Size(value) != attrdef->m_length)
520                 {
521                         PyErr_Format(PyExc_TypeError, "incorrect number of elements in sequence for attribute \"%s\"", attrdef->m_name);
522                         return PY_SET_ATTR_FAIL;
523                 }
524                 switch (attrdef->m_type) 
525                 {
526                 case KX_PYATTRIBUTE_TYPE_FUNCTION:
527                         if (attrdef->m_setFunction == NULL) 
528                         {
529                                 PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name);
530                                 return PY_SET_ATTR_FAIL;
531                         }
532                         return (*attrdef->m_setFunction)(ref, attrdef, value);
533                 case KX_PYATTRIBUTE_TYPE_BOOL:
534                         bufferSize = sizeof(bool);
535                         break;
536                 case KX_PYATTRIBUTE_TYPE_SHORT:
537                         bufferSize = sizeof(short int);
538                         break;
539                 case KX_PYATTRIBUTE_TYPE_ENUM:
540                 case KX_PYATTRIBUTE_TYPE_INT:
541                         bufferSize = sizeof(int);
542                         break;
543                 case KX_PYATTRIBUTE_TYPE_FLOAT:
544                         bufferSize = sizeof(float);
545                         break;
546                 default:
547                         // should not happen
548                         PyErr_Format(PyExc_AttributeError, "Unsupported attribute type for attribute \"%s\", report to blender.org", attrdef->m_name);
549                         return PY_SET_ATTR_FAIL;
550                 }
551                 // let's implement a smart undo method
552                 bufferSize *= attrdef->m_length;
553                 undoBuffer = malloc(bufferSize);
554                 sourceBuffer = ptr;
555                 if (undoBuffer)
556                 {
557                         memcpy(undoBuffer, sourceBuffer, bufferSize);
558                 }
559                 for (int i=0; i<attrdef->m_length; i++)
560                 {
561                         item = PySequence_GetItem(value, i); /* new ref */
562                         switch (attrdef->m_type) 
563                         {
564                         case KX_PYATTRIBUTE_TYPE_BOOL:
565                                 {
566                                         bool *var = reinterpret_cast<bool*>(ptr);
567                                         ptr += sizeof(bool);
568                                         if (PyLong_Check(item)) 
569                                         {
570                                                 *var = (PyLong_AsSsize_t(item) != 0);
571                                         } 
572                                         else if (PyBool_Check(item))
573                                         {
574                                                 *var = (item == Py_True);
575                                         }
576                                         else
577                                         {
578                                                 PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
579                                                 goto UNDO_AND_ERROR;
580                                         }
581                                         break;
582                                 }
583                         case KX_PYATTRIBUTE_TYPE_SHORT:
584                                 {
585                                         short int *var = reinterpret_cast<short int*>(ptr);
586                                         ptr += sizeof(short int);
587                                         if (PyLong_Check(item)) 
588                                         {
589                                                 long val = PyLong_AsSsize_t(item);
590                                                 if (attrdef->m_clamp)
591                                                 {
592                                                         if (val < attrdef->m_imin)
593                                                                 val = attrdef->m_imin;
594                                                         else if (val > attrdef->m_imax)
595                                                                 val = attrdef->m_imax;
596                                                 }
597                                                 else if (val < attrdef->m_imin || val > attrdef->m_imax)
598                                                 {
599                                                         PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
600                                                         goto UNDO_AND_ERROR;
601                                                 }
602                                                 *var = (short int)val;
603                                         }
604                                         else
605                                         {
606                                                 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
607                                                 goto UNDO_AND_ERROR;
608                                         }
609                                         break;
610                                 }
611                         case KX_PYATTRIBUTE_TYPE_ENUM:
612                                 // enum are equivalent to int, just make sure that the field size matches:
613                                 if (sizeof(int) != attrdef->m_size)
614                                 {
615                                         PyErr_Format(PyExc_AttributeError, "Size check error for attribute, \"%s\", report to blender.org", attrdef->m_name);
616                                         goto UNDO_AND_ERROR;
617                                 }
618                                 // walkthrough
619                         case KX_PYATTRIBUTE_TYPE_INT:
620                                 {
621                                         int *var = reinterpret_cast<int*>(ptr);
622                                         ptr += sizeof(int);
623                                         if (PyLong_Check(item)) 
624                                         {
625                                                 long val = PyLong_AsSsize_t(item);
626                                                 if (attrdef->m_clamp)
627                                                 {
628                                                         if (val < attrdef->m_imin)
629                                                                 val = attrdef->m_imin;
630                                                         else if (val > attrdef->m_imax)
631                                                                 val = attrdef->m_imax;
632                                                 }
633                                                 else if (val < attrdef->m_imin || val > attrdef->m_imax)
634                                                 {
635                                                         PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
636                                                         goto UNDO_AND_ERROR;
637                                                 }
638                                                 *var = (int)val;
639                                         }
640                                         else
641                                         {
642                                                 PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
643                                                 goto UNDO_AND_ERROR;
644                                         }
645                                         break;
646                                 }
647                         case KX_PYATTRIBUTE_TYPE_FLOAT:
648                                 {
649                                         float *var = reinterpret_cast<float*>(ptr);
650                                         ptr += sizeof(float);
651                                         double val = PyFloat_AsDouble(item);
652                                         if (val == -1.0 && PyErr_Occurred())
653                                         {
654                                                 PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
655                                                 goto UNDO_AND_ERROR;
656                                         }
657                                         else if (attrdef->m_clamp) 
658                                         {
659                                                 if (val < attrdef->m_fmin)
660                                                         val = attrdef->m_fmin;
661                                                 else if (val > attrdef->m_fmax)
662                                                         val = attrdef->m_fmax;
663                                         }
664                                         else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
665                                         {
666                                                 PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
667                                                 goto UNDO_AND_ERROR;
668                                         }
669                                         *var = (float)val;
670                                         break;
671                                 }
672                         default:
673                                 // should not happen
674                                 PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name);
675                                 goto UNDO_AND_ERROR;
676                         }
677                         // finished using item, release
678                         Py_DECREF(item);
679                         item = NULL;
680                 }
681                 // no error, call check function if any
682                 if (attrdef->m_checkFunction != NULL)
683                 {
684                         if ((*attrdef->m_checkFunction)(ref, attrdef) != 0)
685                         {
686                                 // if the checing function didnt set an error then set a generic one here so we dont set an error with no exception
687                                 if (PyErr_Occurred()==0)
688                                         PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", reasion unknown", attrdef->m_name);
689                                 
690                                 // post check returned an error, restore values
691                         UNDO_AND_ERROR:
692                                 if (undoBuffer)
693                                 {
694                                         memcpy(sourceBuffer, undoBuffer, bufferSize);
695                                         free(undoBuffer);
696                                 }
697                                 if (item)
698                                         Py_DECREF(item);
699                                 return PY_SET_ATTR_FAIL;
700                         }
701                 }
702                 if (undoBuffer)
703                         free(undoBuffer);
704                 return PY_SET_ATTR_SUCCESS;
705         }
706         else    // simple attribute value
707         {
708                 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
709                 {
710                         if (attrdef->m_setFunction == NULL)
711                         {
712                                 PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name);
713                                 return PY_SET_ATTR_FAIL;
714                         }
715                         return (*attrdef->m_setFunction)(ref, attrdef, value);
716                 }
717                 if (attrdef->m_checkFunction != NULL || attrdef->m_type == KX_PYATTRIBUTE_TYPE_VECTOR)
718                 {
719                         // post check function is provided, prepare undo buffer
720                         sourceBuffer = ptr;
721                         switch (attrdef->m_type) 
722                         {
723                         case KX_PYATTRIBUTE_TYPE_BOOL:
724                                 bufferSize = sizeof(bool);
725                                 break;
726                         case KX_PYATTRIBUTE_TYPE_SHORT:
727                                 bufferSize = sizeof(short);
728                                 break;
729                         case KX_PYATTRIBUTE_TYPE_ENUM:
730                         case KX_PYATTRIBUTE_TYPE_FLAG:
731                         case KX_PYATTRIBUTE_TYPE_CHAR:
732                                 bufferSize = attrdef->m_size;
733                                 break;
734                         case KX_PYATTRIBUTE_TYPE_INT:
735                                 bufferSize = sizeof(int);
736                                 break;
737                         case KX_PYATTRIBUTE_TYPE_FLOAT:
738                                 bufferSize = sizeof(float);
739                                 if (attrdef->m_imax)
740                                         bufferSize *= attrdef->m_imax;
741                                 if (attrdef->m_imin)
742                                         bufferSize *= attrdef->m_imin;
743                                 break;
744                         case KX_PYATTRIBUTE_TYPE_STRING:
745                                 sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr();
746                                 if (sourceBuffer)
747                                         bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1;
748                                 break;
749                         case KX_PYATTRIBUTE_TYPE_VECTOR:
750                                 bufferSize = sizeof(MT_Vector3);
751                                 break;
752                         default:
753                                 PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
754                                 return PY_SET_ATTR_FAIL;
755                         }
756                         if (bufferSize)
757                         {
758                                 undoBuffer = malloc(bufferSize);
759                                 if (undoBuffer)
760                                 {
761                                         memcpy(undoBuffer, sourceBuffer, bufferSize);
762                                 }
763                         }
764                 }
765                         
766                 switch (attrdef->m_type) 
767                 {
768                 case KX_PYATTRIBUTE_TYPE_BOOL:
769                         {
770                                 bool *var = reinterpret_cast<bool*>(ptr);
771                                 if (PyLong_Check(value)) 
772                                 {
773                                         *var = (PyLong_AsSsize_t(value) != 0);
774                                 } 
775                                 else if (PyBool_Check(value))
776                                 {
777                                         *var = (value == Py_True);
778                                 }
779                                 else
780                                 {
781                                         PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
782                                         goto FREE_AND_ERROR;
783                                 }
784                                 break;
785                         }
786                 case KX_PYATTRIBUTE_TYPE_FLAG:
787                         {
788                                 bool bval;
789                                 if (PyLong_Check(value)) 
790                                 {
791                                         bval = (PyLong_AsSsize_t(value) != 0);
792                                 } 
793                                 else if (PyBool_Check(value))
794                                 {
795                                         bval = (value == Py_True);
796                                 }
797                                 else
798                                 {
799                                         PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
800                                         goto FREE_AND_ERROR;
801                                 }
802                                 if (attrdef->m_imax)
803                                         bval = !bval;
804                                 switch (attrdef->m_size) {
805                                 case 1:
806                                         {
807                                                 unsigned char *val = reinterpret_cast<unsigned char*>(ptr);
808                                                 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
809                                                 break;
810                                         }
811                                 case 2:
812                                         {
813                                                 unsigned short *val = reinterpret_cast<unsigned short*>(ptr);
814                                                 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
815                                                 break;
816                                         }
817                                 case 4:
818                                         {
819                                                 unsigned int *val = reinterpret_cast<unsigned int*>(ptr);
820                                                 *val = (*val & ~attrdef->m_imin) | ((bval)?attrdef->m_imin:0);
821                                                 break;
822                                         }
823                                 default:
824                                         PyErr_Format(PyExc_TypeError, "internal error: unsupported flag field \"%s\"", attrdef->m_name);
825                                         goto FREE_AND_ERROR;
826                                 }
827                                 break;
828                         }
829                 case KX_PYATTRIBUTE_TYPE_SHORT:
830                         {
831                                 short int *var = reinterpret_cast<short int*>(ptr);
832                                 if (PyLong_Check(value)) 
833                                 {
834                                         long val = PyLong_AsSsize_t(value);
835                                         if (attrdef->m_clamp)
836                                         {
837                                                 if (val < attrdef->m_imin)
838                                                         val = attrdef->m_imin;
839                                                 else if (val > attrdef->m_imax)
840                                                         val = attrdef->m_imax;
841                                         }
842                                         else if (val < attrdef->m_imin || val > attrdef->m_imax)
843                                         {
844                                                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
845                                                 goto FREE_AND_ERROR;
846                                         }
847                                         *var = (short int)val;
848                                 }
849                                 else
850                                 {
851                                         PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
852                                         goto FREE_AND_ERROR;
853                                 }
854                                 break;
855                         }
856                 case KX_PYATTRIBUTE_TYPE_ENUM:
857                         // enum are equivalent to int, just make sure that the field size matches:
858                         if (sizeof(int) != attrdef->m_size)
859                         {
860                                 PyErr_Format(PyExc_AttributeError, "attribute size check error for attribute \"%s\", report to blender.org", attrdef->m_name);
861                                 goto FREE_AND_ERROR;
862                         }
863                         // walkthrough
864                 case KX_PYATTRIBUTE_TYPE_INT:
865                         {
866                                 int *var = reinterpret_cast<int*>(ptr);
867                                 if (PyLong_Check(value)) 
868                                 {
869                                         long val = PyLong_AsSsize_t(value);
870                                         if (attrdef->m_clamp)
871                                         {
872                                                 if (val < attrdef->m_imin)
873                                                         val = attrdef->m_imin;
874                                                 else if (val > attrdef->m_imax)
875                                                         val = attrdef->m_imax;
876                                         }
877                                         else if (val < attrdef->m_imin || val > attrdef->m_imax)
878                                         {
879                                                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
880                                                 goto FREE_AND_ERROR;
881                                         }
882                                         *var = (int)val;
883                                 }
884                                 else
885                                 {
886                                         PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
887                                         goto FREE_AND_ERROR;
888                                 }
889                                 break;
890                         }
891                 case KX_PYATTRIBUTE_TYPE_FLOAT:
892                         {
893                                 float *var = reinterpret_cast<float*>(ptr);
894                                 if (attrdef->m_imin != 0) 
895                                 {
896                                         if (attrdef->m_size != attrdef->m_imin*attrdef->m_imax*sizeof(float)) 
897                                         {
898                                                 PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name);
899                                                 goto FREE_AND_ERROR;
900                                         }
901                                         if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imin) 
902                                         {
903                                                 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
904                                                 goto FREE_AND_ERROR;
905                                         }
906                                         for (int i=0; i<attrdef->m_imin; i++)
907                                         {
908                                                 PyObject *list = PySequence_GetItem(value, i); /* new ref */
909                                                 if (!PySequence_Check(list) || PySequence_Size(list) != attrdef->m_imax) 
910                                                 {
911                                                         PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
912                                                         goto RESTORE_AND_ERROR;
913                                                 }
914                                                 for (int j=0; j<attrdef->m_imax; j++)
915                                                 {
916                                                         item = PySequence_GetItem(list, j); /* new ref */
917                                                         if (!py_check_attr_float(var, item, attrdef))
918                                                         {
919                                                                 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d][%d] floats for attribute \"%s\"", attrdef->m_imin, attrdef->m_imax, attrdef->m_name);
920                                                                 goto RESTORE_AND_ERROR;
921                                                         }
922                                                         Py_DECREF(item);
923                                                         item = NULL;
924                                                         ++var;
925                                                 }
926                                                 Py_DECREF(list);
927                                                 list = NULL;
928                                         }
929                                 } 
930                                 else if (attrdef->m_imax != 0) 
931                                 {
932                                         if (attrdef->m_size != attrdef->m_imax*sizeof(float)) 
933                                         {
934                                                 PyErr_Format(PyExc_TypeError, "internal error: incorrect field size for attribute \"%s\"", attrdef->m_name);
935                                                 goto FREE_AND_ERROR;
936                                         }
937                                         if (!PySequence_Check(value) || PySequence_Size(value) != attrdef->m_imax) 
938                                         {
939                                                 PyErr_Format(PyExc_TypeError, "expected a sequence of [%d] floats for attribute \"%s\"", attrdef->m_imax, attrdef->m_name);
940                                                 goto FREE_AND_ERROR;
941                                         }
942                                         for (int i=0; i<attrdef->m_imax; i++)
943                                         {
944                                                 item = PySequence_GetItem(value, i); /* new ref */
945                                                 if (!py_check_attr_float(var, item, attrdef))
946                                                 {
947                                                         goto RESTORE_AND_ERROR;
948                                                 }
949                                                 Py_DECREF(item);
950                                                 item = NULL;
951                                                 ++var;
952                                         }
953                                 } 
954                                 else
955                                 {
956                                         if (!py_check_attr_float(var, value, attrdef))
957                                                 goto FREE_AND_ERROR;
958                                 }
959                                 break;
960                         }
961                 case KX_PYATTRIBUTE_TYPE_VECTOR:
962                         {
963                                 if (!PySequence_Check(value) || PySequence_Size(value) != 3) 
964                                 {
965                                         PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
966                                         goto FREE_AND_ERROR;
967                                 }
968                                 MT_Vector3 *var = reinterpret_cast<MT_Vector3*>(ptr);
969                                 for (int i=0; i<3; i++)
970                                 {
971                                         item = PySequence_GetItem(value, i); /* new ref */
972                                         double val = PyFloat_AsDouble(item);
973                                         Py_DECREF(item);
974                                         item = NULL;
975                                         if (val == -1.0 && PyErr_Occurred())
976                                         {
977                                                 PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
978                                                 goto RESTORE_AND_ERROR;
979                                         }
980                                         else if (attrdef->m_clamp)
981                                         {
982                                                 if (val < attrdef->m_fmin)
983                                                         val = attrdef->m_fmin;
984                                                 else if (val > attrdef->m_fmax)
985                                                         val = attrdef->m_fmax;
986                                         }
987                                         else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
988                                         {
989                                                 PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
990                                                 goto RESTORE_AND_ERROR;
991                                         }
992                                         (*var)[i] = (MT_Scalar)val;
993                                 }
994                                 break;
995                         }
996                 case KX_PYATTRIBUTE_TYPE_CHAR:
997                         {
998                                 if (PyUnicode_Check(value)) 
999                                 {
1000                                         Py_ssize_t val_len;
1001                                         char *val = _PyUnicode_AsStringAndSize(value, &val_len);
1002                                         strncpy(ptr, val, attrdef->m_size);
1003                                         ptr[attrdef->m_size-1] = 0;
1004                                 }
1005                                 else
1006                                 {
1007                                         PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name);
1008                                         goto FREE_AND_ERROR;
1009                                 }
1010                                 break;
1011                         }
1012                 case KX_PYATTRIBUTE_TYPE_STRING:
1013                         {
1014                                 STR_String *var = reinterpret_cast<STR_String*>(ptr);
1015                                 if (PyUnicode_Check(value)) 
1016                                 {
1017                                         Py_ssize_t val_len;
1018                                         char *val = _PyUnicode_AsStringAndSize(value, &val_len);
1019                                         if (attrdef->m_clamp)
1020                                         {
1021                                                 if (val_len < attrdef->m_imin)
1022                                                 {
1023                                                         // can't increase the length of the string
1024                                                         PyErr_Format(PyExc_ValueError, "string length too short for attribute \"%s\"", attrdef->m_name);
1025                                                         goto FREE_AND_ERROR;
1026                                                 }
1027                                                 else if (val_len > attrdef->m_imax)
1028                                                 {
1029                                                         // trim the string
1030                                                         char c = val[attrdef->m_imax];
1031                                                         val[attrdef->m_imax] = 0;
1032                                                         *var = val;
1033                                                         val[attrdef->m_imax] = c;
1034                                                         break;
1035                                                 }
1036                                         } else if (val_len < attrdef->m_imin || val_len > attrdef->m_imax)
1037                                         {
1038                                                 PyErr_Format(PyExc_ValueError, "string length out of range for attribute \"%s\"", attrdef->m_name);
1039                                                 goto FREE_AND_ERROR;
1040                                         }
1041                                         *var = val;
1042                                 }
1043                                 else
1044                                 {
1045                                         PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name);
1046                                         goto FREE_AND_ERROR;
1047                                 }
1048                                 break;
1049                         }
1050                 default:
1051                         // should not happen
1052                         PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
1053                         goto FREE_AND_ERROR;
1054                 }
1055         }
1056         // check if post processing is needed
1057         if (attrdef->m_checkFunction != NULL)
1058         {
1059                 if ((*attrdef->m_checkFunction)(ref, attrdef) != 0)
1060                 {
1061                         // restore value
1062                 RESTORE_AND_ERROR:
1063                         if (undoBuffer)
1064                         {
1065                                 if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING)
1066                                 {
1067                                         // special case for STR_String: restore the string
1068                                         STR_String *var = reinterpret_cast<STR_String*>(ptr);
1069                                         *var = reinterpret_cast<char*>(undoBuffer);
1070                                 }
1071                                 else
1072                                 {
1073                                         // other field type have direct values
1074                                         memcpy(ptr, undoBuffer, bufferSize);
1075                                 }
1076                         }
1077                 FREE_AND_ERROR:
1078                         if (undoBuffer)
1079                                 free(undoBuffer);
1080                         if (list)
1081                                 Py_DECREF(list);
1082                         if (item)
1083                                 Py_DECREF(item);
1084                         return 1;
1085                 }
1086         }
1087         if (undoBuffer)
1088                 free(undoBuffer);
1089         return 0;       
1090 }
1091
1092
1093
1094 /*------------------------------
1095  * PyObjectPlus repr            -- representations
1096 ------------------------------*/
1097 PyObject *PyObjectPlus::py_repr(void)
1098 {
1099         PyErr_SetString(PyExc_SystemError, "Representation not overridden by object.");  
1100         return NULL;
1101 }
1102
1103 PyObject *PyObjectPlus::GetProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr)
1104 {
1105         if (self->m_proxy==NULL)
1106         {
1107                 self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
1108                 BGE_PROXY_PYOWNS(self->m_proxy) = false;
1109                 BGE_PROXY_PYREF(self->m_proxy) = true;
1110         }
1111         //PyObject_Print(self->m_proxy, stdout, 0);
1112         //printf("ref %d\n", self->m_proxy->ob_refcnt);
1113         
1114         BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */
1115         BGE_PROXY_PTR(self->m_proxy) = ptr;
1116         Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */
1117         return self->m_proxy;
1118 }
1119
1120 PyObject *PyObjectPlus::NewProxyPlus_Ext(PyObjectPlus *self, PyTypeObject *tp, void *ptr, bool py_owns)
1121 {
1122         if (!self) 
1123         {
1124                 // in case of proxy without reference to game object
1125                 PyObject* proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
1126                 BGE_PROXY_PYREF(proxy) = false;
1127                 BGE_PROXY_PYOWNS(proxy) = py_owns;
1128                 BGE_PROXY_REF(proxy) = NULL; 
1129                 BGE_PROXY_PTR(proxy) = ptr;
1130                 return proxy;
1131         }
1132         if (self->m_proxy)
1133         {
1134                 if(py_owns)
1135                 {       /* Free */
1136                         BGE_PROXY_REF(self->m_proxy) = NULL;
1137                         Py_DECREF(self->m_proxy);
1138                         self->m_proxy= NULL;
1139                 }
1140                 else {
1141                         Py_INCREF(self->m_proxy);
1142                         return self->m_proxy;
1143                 }
1144                 
1145         }
1146         
1147         GetProxyPlus_Ext(self, tp, ptr);
1148         if(py_owns) {
1149                 BGE_PROXY_PYOWNS(self->m_proxy) = py_owns;
1150                 Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */
1151         }
1152         return self->m_proxy;
1153 }
1154
1155 ///////////////////////////////////////////////////////////////////////////////////////////////
1156 ///////////////////////////////////////////////////////////////////////////////////////////////
1157 /* deprecation warning management */
1158
1159 bool PyObjectPlus::m_ignore_deprecation_warnings(false);
1160 void PyObjectPlus::SetDeprecationWarnings(bool ignoreDeprecationWarnings)
1161 {
1162         m_ignore_deprecation_warnings = ignoreDeprecationWarnings;
1163 }
1164
1165 void PyDebugLine()
1166 {
1167         // import sys; print '\t%s:%d' % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_lineno)
1168
1169         PyObject *getframe, *frame;
1170         PyObject *f_lineno, *f_code, *co_filename;
1171
1172         getframe = PySys_GetObject((char *)"_getframe"); // borrowed
1173         if (getframe) {
1174                 frame = PyObject_CallObject(getframe, NULL);
1175                 if (frame) {
1176                         f_lineno= PyObject_GetAttrString(frame, "f_lineno");
1177                         f_code= PyObject_GetAttrString(frame, "f_code");
1178                         if (f_lineno && f_code) {
1179                                 co_filename= PyObject_GetAttrString(f_code, "co_filename");
1180                                 if (co_filename) {
1181
1182                                         printf("\t%s:%d\n", _PyUnicode_AsString(co_filename), (int)PyLong_AsSsize_t(f_lineno));
1183
1184                                         Py_DECREF(f_lineno);
1185                                         Py_DECREF(f_code);
1186                                         Py_DECREF(co_filename);
1187                                         Py_DECREF(frame);
1188                                         return;
1189                                 }
1190                         }
1191                         
1192                         Py_XDECREF(f_lineno);
1193                         Py_XDECREF(f_code);
1194                         Py_DECREF(frame);
1195                 }
1196
1197         }
1198         PyErr_Clear();
1199         printf("\tERROR - Could not access sys._getframe(0).f_lineno or sys._getframe().f_code.co_filename\n");
1200 }
1201
1202 void PyObjectPlus::ShowDeprecationWarning_func(const char* old_way,const char* new_way)
1203 {
1204         printf("Method %s is deprecated, please use %s instead.\n", old_way, new_way);
1205         PyDebugLine();
1206 }
1207
1208 void PyObjectPlus::ClearDeprecationWarning()
1209 {
1210         WarnLink *wlink_next;
1211         WarnLink *wlink = GetDeprecationWarningLinkFirst();
1212         
1213         while(wlink)
1214         {
1215                 wlink->warn_done= false; /* no need to NULL the link, its cleared before adding to the list next time round */
1216                 wlink_next= reinterpret_cast<WarnLink *>(wlink->link);
1217                 wlink->link= NULL;
1218                 wlink= wlink_next;
1219         }
1220         NullDeprecationWarning();
1221 }
1222
1223 WarnLink*               m_base_wlink_first= NULL;
1224 WarnLink*               m_base_wlink_last= NULL;
1225
1226 WarnLink*               PyObjectPlus::GetDeprecationWarningLinkFirst(void) {return m_base_wlink_first;}
1227 WarnLink*               PyObjectPlus::GetDeprecationWarningLinkLast(void) {return m_base_wlink_last;}
1228 void                    PyObjectPlus::SetDeprecationWarningFirst(WarnLink* wlink) {m_base_wlink_first= wlink;}
1229 void                    PyObjectPlus::SetDeprecationWarningLinkLast(WarnLink* wlink) {m_base_wlink_last= wlink;}
1230 void                    PyObjectPlus::NullDeprecationWarning() {m_base_wlink_first= m_base_wlink_last= NULL;}
1231
1232 #endif // WITH_PYTHON