~ubuntu-branches/ubuntu/lucid/blender/lucid

« back to all changes in this revision

Viewing changes to source/gameengine/Expressions/PyObjectPlus.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Chris Coulson
  • Date: 2009-08-06 22:32:19 UTC
  • mfrom: (1.2.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20090806223219-8z4eej1u8levu4pz
Tags: 2.49a+dfsg-0ubuntu1
* Merge from debian unstable, remaining changes:
  - debian/control: Build-depend on python-2.6 rather than python-2.5.
  - debian/misc/*.desktop: Add Spanish translation to .desktop 
    files.
  - debian/pyversions: 2.6.
  - debian/rules: Clean *.o of source/blender/python/api2_2x/
* New upstream release (LP: #382153).
* Refreshed patches:
  - 01_sanitize_sys.patch
  - 02_tmp_in_HOME
  - 10_use_systemwide_ftgl
  - 70_portability_platform_detection
* Removed patches merged upstream:
  - 30_fix_python_syntax_warning
  - 90_ubuntu_ffmpeg_52_changes

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/**
2
 
 * $Id: PyObjectPlus.cpp 16395 2008-09-06 14:13:31Z sirdude $
 
2
 * $Id: PyObjectPlus.cpp 20420 2009-05-26 07:41:34Z campbellbarton $
3
3
 * ***** BEGIN GPL LICENSE BLOCK *****
4
4
 *
5
5
 * This program is free software; you can redistribute it and/or
50
50
#include "stdlib.h"
51
51
#include "PyObjectPlus.h"
52
52
#include "STR_String.h"
 
53
#include "MT_Vector3.h"
53
54
/*------------------------------
54
55
 * PyObjectPlus Type            -- Every class, even the abstract one should have a Type
55
56
------------------------------*/
56
57
 
 
58
 
57
59
PyTypeObject PyObjectPlus::Type = {
58
 
        PyObject_HEAD_INIT(&PyType_Type)
 
60
#if (PY_VERSION_HEX >= 0x02060000)
 
61
        PyVarObject_HEAD_INIT(NULL, 0)
 
62
#else
 
63
        /* python 2.5 and below */
 
64
        PyObject_HEAD_INIT( NULL )  /* required py macro */
59
65
        0,                              /*ob_size*/
 
66
#endif
60
67
        "PyObjectPlus",                 /*tp_name*/
61
 
        sizeof(PyObjectPlus),           /*tp_basicsize*/
 
68
        sizeof(PyObjectPlus_Proxy),             /*tp_basicsize*/
62
69
        0,                              /*tp_itemsize*/
63
70
        /* methods */
64
 
        PyDestructor,                   /*tp_dealloc*/
65
 
        0,                              /*tp_print*/
66
 
        __getattr,                      /*tp_getattr*/
67
 
        __setattr,                      /*tp_setattr*/
68
 
        0,                              /*tp_compare*/
69
 
        __repr,                         /*tp_repr*/
70
 
        0,                              /*tp_as_number*/
71
 
        0,                              /*tp_as_sequence*/
72
 
        0,                              /*tp_as_mapping*/
73
 
        0,                              /*tp_hash*/
74
 
        0,                              /*tp_call */
 
71
        py_base_dealloc,
 
72
        0,
 
73
        0,
 
74
        0,
 
75
        0,
 
76
        py_base_repr,
 
77
        0,0,0,0,0,0,
 
78
        py_base_getattro,
 
79
        py_base_setattro,
 
80
        0,0,0,0,0,0,0,0,0,
 
81
        Methods
75
82
};
76
83
 
 
84
 
77
85
PyObjectPlus::~PyObjectPlus()
78
86
{
79
 
        if (ob_refcnt)
80
 
        {
81
 
                _Py_ForgetReference(this);
 
87
        if(m_proxy) {
 
88
                Py_DECREF(m_proxy);                     /* Remove own reference, python may still have 1 */
 
89
                BGE_PROXY_REF(m_proxy)= NULL;
82
90
        }
83
91
//      assert(ob_refcnt==0);
84
92
}
85
93
 
86
 
PyObjectPlus::PyObjectPlus(PyTypeObject *T)                             // constructor
 
94
void PyObjectPlus::py_base_dealloc(PyObject *self)                              // python wrapper
 
95
{
 
96
        PyObjectPlus *self_plus= BGE_PROXY_REF(self);
 
97
        if(self_plus) {
 
98
                if(BGE_PROXY_PYOWNS(self)) { /* Does python own this?, then delete it  */
 
99
                        self_plus->m_proxy = NULL; /* Need this to stop ~PyObjectPlus from decrefing m_proxy otherwise its decref'd twice and py-debug crashes */
 
100
                        delete self_plus;
 
101
                }
 
102
                
 
103
                BGE_PROXY_REF(self)= NULL; // not really needed
 
104
        }
 
105
        PyObject_DEL( self );
 
106
};
 
107
 
 
108
PyObjectPlus::PyObjectPlus(PyTypeObject *T) : SG_QList()                                // constructor
87
109
{
88
110
        MT_assert(T != NULL);
89
 
        this->ob_type = T; 
90
 
        _Py_NewReference(this);
 
111
        m_proxy= NULL;
91
112
};
92
113
  
93
114
/*------------------------------
94
115
 * PyObjectPlus Methods         -- Every class, even the abstract one should have a Methods
95
116
------------------------------*/
96
117
PyMethodDef PyObjectPlus::Methods[] = {
97
 
  {"isA",                (PyCFunction) sPy_isA,                 METH_VARARGS},
 
118
  {"isA",                (PyCFunction) sPyisA,                  METH_O},
98
119
  {NULL, NULL}          /* Sentinel */
99
120
};
100
121
 
 
122
PyAttributeDef PyObjectPlus::Attributes[] = {
 
123
        KX_PYATTRIBUTE_RO_FUNCTION("invalid",           PyObjectPlus, pyattr_get_invalid),
 
124
        {NULL} //Sentinel
 
125
};
 
126
 
 
127
PyObject* PyObjectPlus::pyattr_get_invalid(void *self_v, const KX_PYATTRIBUTE_DEF *attrdef)
 
128
{       
 
129
        Py_RETURN_FALSE;
 
130
}
 
131
 
101
132
/*------------------------------
102
133
 * PyObjectPlus Parents         -- Every class, even the abstract one should have parents
103
134
------------------------------*/
106
137
/*------------------------------
107
138
 * PyObjectPlus attributes      -- attributes
108
139
------------------------------*/
109
 
PyObject *PyObjectPlus::_getattr(const STR_String& attr)
110
 
{
111
 
        if (attr == "__doc__" && GetType()->tp_doc)
112
 
                return PyString_FromString(GetType()->tp_doc);
113
 
 
114
 
  //if (streq(attr, "type"))
115
 
  //  return Py_BuildValue("s", (*(GetParents()))->tp_name);
116
 
 
117
 
  return Py_FindMethod(Methods, this, const_cast<char *>(attr.ReadPtr()));
118
 
}
119
 
 
120
 
int PyObjectPlus::_delattr(const STR_String& attr)
 
140
 
 
141
 
 
142
/* This should be the entry in Type since it takes the C++ class from PyObjectPlus_Proxy */
 
143
PyObject *PyObjectPlus::py_base_getattro(PyObject * self, PyObject *attr)
 
144
{
 
145
        PyObjectPlus *self_plus= BGE_PROXY_REF(self);
 
146
        if(self_plus==NULL) {
 
147
                if(!strcmp("invalid", PyString_AsString(attr))) {
 
148
                        Py_RETURN_TRUE;
 
149
                }
 
150
                PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
 
151
                return NULL;
 
152
        }
 
153
        
 
154
        PyObject *ret= self_plus->py_getattro(attr);
 
155
        
 
156
        /* Attribute not found, was this a __dict__ lookup?, otherwise set an error if none is set */
 
157
        if(ret==NULL) {
 
158
                char *attr_str= PyString_AsString(attr);
 
159
                
 
160
                if (strcmp(attr_str, "__dict__")==0)
 
161
                {
 
162
                        /* the error string will probably not
 
163
                         * be set but just incase clear it */
 
164
                        PyErr_Clear(); 
 
165
                        ret= self_plus->py_getattro_dict();
 
166
                }
 
167
                else if (!PyErr_Occurred()) {
 
168
                        /* We looked for an attribute but it wasnt found
 
169
                         * since py_getattro didnt set the error, set it here */
 
170
                        PyErr_Format(PyExc_AttributeError, "'%s' object has no attribute '%s'", self->ob_type->tp_name, attr_str);
 
171
                }
 
172
        }
 
173
        return ret;
 
174
}
 
175
 
 
176
/* This should be the entry in Type since it takes the C++ class from PyObjectPlus_Proxy */
 
177
int PyObjectPlus::py_base_setattro(PyObject *self, PyObject *attr, PyObject *value)
 
178
{
 
179
        PyObjectPlus *self_plus= BGE_PROXY_REF(self);
 
180
        if(self_plus==NULL) {
 
181
                PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
 
182
                return -1;
 
183
        }
 
184
        
 
185
        if (value==NULL)
 
186
                return self_plus->py_delattro(attr);
 
187
        
 
188
        return self_plus->py_setattro(attr, value); 
 
189
}
 
190
 
 
191
PyObject *PyObjectPlus::py_base_repr(PyObject *self)                    // This should be the entry in Type.
 
192
{
 
193
        
 
194
        PyObjectPlus *self_plus= BGE_PROXY_REF(self);
 
195
        if(self_plus==NULL) {
 
196
                PyErr_SetString(PyExc_SystemError, BGE_PROXY_ERROR_MSG);
 
197
                return NULL;
 
198
        }
 
199
        
 
200
        return self_plus->py_repr();  
 
201
}
 
202
 
 
203
PyObject *PyObjectPlus::py_getattro(PyObject* attr)
 
204
{
 
205
        PyObject *descr = PyDict_GetItem(Type.tp_dict, attr); \
 
206
        if (descr == NULL) {
 
207
                return NULL; /* py_base_getattro sets the error, this way we can avoid setting the error at many levels */
 
208
        } else {
 
209
                /* Copied from py_getattro_up */
 
210
                if (PyCObject_Check(descr)) {
 
211
                        return py_get_attrdef((void *)this, (const PyAttributeDef*)PyCObject_AsVoidPtr(descr));
 
212
                } else if (descr->ob_type->tp_descr_get) {
 
213
                        return PyCFunction_New(((PyMethodDescrObject *)descr)->d_method, this->m_proxy);
 
214
                } else {
 
215
                        return NULL;
 
216
                }
 
217
                /* end py_getattro_up copy */
 
218
        }
 
219
}
 
220
 
 
221
PyObject* PyObjectPlus::py_getattro_dict() {
 
222
        return py_getattr_dict(NULL, Type.tp_dict);
 
223
}
 
224
 
 
225
int PyObjectPlus::py_delattro(PyObject* attr)
121
226
{
122
227
        PyErr_SetString(PyExc_AttributeError, "attribute cant be deleted");
123
228
        return 1;
124
229
}
125
230
 
126
 
int PyObjectPlus::_setattr(const STR_String& attr, PyObject *value)
 
231
int PyObjectPlus::py_setattro(PyObject *attr, PyObject* value)
127
232
{
128
 
        //return PyObject::_setattr(attr,value);
129
 
        //cerr << "Unknown attribute" << endl;
130
233
        PyErr_SetString(PyExc_AttributeError, "attribute cant be set");
131
 
        return 1;
132
 
}
 
234
        return PY_SET_ATTR_MISSING;
 
235
}
 
236
 
 
237
PyObject *PyObjectPlus::py_get_attrdef(void *self, const PyAttributeDef *attrdef)
 
238
{
 
239
        if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_DUMMY)
 
240
        {
 
241
                // fake attribute, ignore
 
242
                return NULL;
 
243
        }
 
244
        if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
 
245
        {
 
246
                // the attribute has no field correspondance, handover processing to function.
 
247
                if (attrdef->m_getFunction == NULL)
 
248
                        return NULL;
 
249
                return (*attrdef->m_getFunction)(self, attrdef);
 
250
        }
 
251
        char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
 
252
        if (attrdef->m_length > 1)
 
253
        {
 
254
                PyObject* resultlist = PyList_New(attrdef->m_length);
 
255
                for (unsigned int i=0; i<attrdef->m_length; i++)
 
256
                {
 
257
                        switch (attrdef->m_type) {
 
258
                        case KX_PYATTRIBUTE_TYPE_BOOL:
 
259
                                {
 
260
                                        bool *val = reinterpret_cast<bool*>(ptr);
 
261
                                        ptr += sizeof(bool);
 
262
                                        PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
 
263
                                        break;
 
264
                                }
 
265
                        case KX_PYATTRIBUTE_TYPE_SHORT:
 
266
                                {
 
267
                                        short int *val = reinterpret_cast<short int*>(ptr);
 
268
                                        ptr += sizeof(short int);
 
269
                                        PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
 
270
                                        break;
 
271
                                }
 
272
                        case KX_PYATTRIBUTE_TYPE_ENUM:
 
273
                                // enum are like int, just make sure the field size is the same
 
274
                                if (sizeof(int) != attrdef->m_size)
 
275
                                {
 
276
                                        Py_DECREF(resultlist);
 
277
                                        return NULL;
 
278
                                }
 
279
                                // walkthrough
 
280
                        case KX_PYATTRIBUTE_TYPE_INT:
 
281
                                {
 
282
                                        int *val = reinterpret_cast<int*>(ptr);
 
283
                                        ptr += sizeof(int);
 
284
                                        PyList_SET_ITEM(resultlist,i,PyInt_FromLong(*val));
 
285
                                        break;
 
286
                                }
 
287
                        case KX_PYATTRIBUTE_TYPE_FLOAT:
 
288
                                {
 
289
                                        float *val = reinterpret_cast<float*>(ptr);
 
290
                                        ptr += sizeof(float);
 
291
                                        PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble(*val));
 
292
                                        break;
 
293
                                }
 
294
                        default:
 
295
                                // no support for array of complex data
 
296
                                Py_DECREF(resultlist);
 
297
                                return NULL;
 
298
                        }
 
299
                }
 
300
                return resultlist;
 
301
        }
 
302
        else
 
303
        {
 
304
                switch (attrdef->m_type) {
 
305
                case KX_PYATTRIBUTE_TYPE_BOOL:
 
306
                        {
 
307
                                bool *val = reinterpret_cast<bool*>(ptr);
 
308
                                return PyInt_FromLong(*val);
 
309
                        }
 
310
                case KX_PYATTRIBUTE_TYPE_SHORT:
 
311
                        {
 
312
                                short int *val = reinterpret_cast<short int*>(ptr);
 
313
                                return PyInt_FromLong(*val);
 
314
                        }
 
315
                case KX_PYATTRIBUTE_TYPE_ENUM:
 
316
                        // enum are like int, just make sure the field size is the same
 
317
                        if (sizeof(int) != attrdef->m_size)
 
318
                        {
 
319
                                return NULL;
 
320
                        }
 
321
                        // walkthrough
 
322
                case KX_PYATTRIBUTE_TYPE_INT:
 
323
                        {
 
324
                                int *val = reinterpret_cast<int*>(ptr);
 
325
                                return PyInt_FromLong(*val);
 
326
                        }
 
327
                case KX_PYATTRIBUTE_TYPE_FLOAT:
 
328
                        {
 
329
                                float *val = reinterpret_cast<float*>(ptr);
 
330
                                return PyFloat_FromDouble(*val);
 
331
                        }
 
332
                case KX_PYATTRIBUTE_TYPE_VECTOR:
 
333
                        {
 
334
                                PyObject* resultlist = PyList_New(3);
 
335
                                MT_Vector3 *val = reinterpret_cast<MT_Vector3*>(ptr);
 
336
                                for (unsigned int i=0; i<3; i++)
 
337
                                {
 
338
                                        PyList_SET_ITEM(resultlist,i,PyFloat_FromDouble((*val)[i]));
 
339
                                }
 
340
                                return resultlist;
 
341
                        }
 
342
                case KX_PYATTRIBUTE_TYPE_STRING:
 
343
                        {
 
344
                                STR_String *val = reinterpret_cast<STR_String*>(ptr);
 
345
                                return PyString_FromString(*val);
 
346
                        }
 
347
                default:
 
348
                        return NULL;
 
349
                }
 
350
        }
 
351
}
 
352
 
 
353
int PyObjectPlus::py_set_attrdef(void *self, const PyAttributeDef *attrdef, PyObject *value)
 
354
{
 
355
        void *undoBuffer = NULL;
 
356
        void *sourceBuffer = NULL;
 
357
        size_t bufferSize = 0;
 
358
        
 
359
        char *ptr = reinterpret_cast<char*>(self)+attrdef->m_offset;
 
360
        if (attrdef->m_length > 1)
 
361
        {
 
362
                if (!PySequence_Check(value)) 
 
363
                {
 
364
                        PyErr_Format(PyExc_TypeError, "expected a sequence for attribute \"%s\"", attrdef->m_name);
 
365
                        return PY_SET_ATTR_FAIL;
 
366
                }
 
367
                if (PySequence_Size(value) != attrdef->m_length)
 
368
                {
 
369
                        PyErr_Format(PyExc_TypeError, "incorrect number of elements in sequence for attribute \"%s\"", attrdef->m_name);
 
370
                        return PY_SET_ATTR_FAIL;
 
371
                }
 
372
                switch (attrdef->m_type) 
 
373
                {
 
374
                case KX_PYATTRIBUTE_TYPE_FUNCTION:
 
375
                        if (attrdef->m_setFunction == NULL) 
 
376
                        {
 
377
                                PyErr_Format(PyExc_AttributeError, "function attribute without function for attribute \"%s\", report to blender.org", attrdef->m_name);
 
378
                                return PY_SET_ATTR_FAIL;
 
379
                        }
 
380
                        return (*attrdef->m_setFunction)(self, attrdef, value);
 
381
                case KX_PYATTRIBUTE_TYPE_BOOL:
 
382
                        bufferSize = sizeof(bool);
 
383
                        break;
 
384
                case KX_PYATTRIBUTE_TYPE_SHORT:
 
385
                        bufferSize = sizeof(short int);
 
386
                        break;
 
387
                case KX_PYATTRIBUTE_TYPE_ENUM:
 
388
                case KX_PYATTRIBUTE_TYPE_INT:
 
389
                        bufferSize = sizeof(int);
 
390
                        break;
 
391
                case KX_PYATTRIBUTE_TYPE_FLOAT:
 
392
                        bufferSize = sizeof(float);
 
393
                        break;
 
394
                default:
 
395
                        // should not happen
 
396
                        PyErr_Format(PyExc_AttributeError, "Unsupported attribute type for attribute \"%s\", report to blender.org", attrdef->m_name);
 
397
                        return PY_SET_ATTR_FAIL;
 
398
                }
 
399
                // let's implement a smart undo method
 
400
                bufferSize *= attrdef->m_length;
 
401
                undoBuffer = malloc(bufferSize);
 
402
                sourceBuffer = ptr;
 
403
                if (undoBuffer)
 
404
                {
 
405
                        memcpy(undoBuffer, sourceBuffer, bufferSize);
 
406
                }
 
407
                for (int i=0; i<attrdef->m_length; i++)
 
408
                {
 
409
                        PyObject *item = PySequence_GetItem(value, i); /* new ref */
 
410
                        // we can decrement the reference immediately, the reference count
 
411
                        // is at least 1 because the item is part of an array
 
412
                        Py_DECREF(item);
 
413
                        switch (attrdef->m_type) 
 
414
                        {
 
415
                        case KX_PYATTRIBUTE_TYPE_BOOL:
 
416
                                {
 
417
                                        bool *var = reinterpret_cast<bool*>(ptr);
 
418
                                        ptr += sizeof(bool);
 
419
                                        if (PyInt_Check(item)) 
 
420
                                        {
 
421
                                                *var = (PyInt_AsLong(item) != 0);
 
422
                                        } 
 
423
                                        else if (PyBool_Check(item))
 
424
                                        {
 
425
                                                *var = (item == Py_True);
 
426
                                        }
 
427
                                        else
 
428
                                        {
 
429
                                                PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
 
430
                                                goto UNDO_AND_ERROR;
 
431
                                        }
 
432
                                        break;
 
433
                                }
 
434
                        case KX_PYATTRIBUTE_TYPE_SHORT:
 
435
                                {
 
436
                                        short int *var = reinterpret_cast<short int*>(ptr);
 
437
                                        ptr += sizeof(short int);
 
438
                                        if (PyInt_Check(item)) 
 
439
                                        {
 
440
                                                long val = PyInt_AsLong(item);
 
441
                                                if (attrdef->m_clamp)
 
442
                                                {
 
443
                                                        if (val < attrdef->m_imin)
 
444
                                                                val = attrdef->m_imin;
 
445
                                                        else if (val > attrdef->m_imax)
 
446
                                                                val = attrdef->m_imax;
 
447
                                                }
 
448
                                                else if (val < attrdef->m_imin || val > attrdef->m_imax)
 
449
                                                {
 
450
                                                        PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
 
451
                                                        goto UNDO_AND_ERROR;
 
452
                                                }
 
453
                                                *var = (short int)val;
 
454
                                        }
 
455
                                        else
 
456
                                        {
 
457
                                                PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
 
458
                                                goto UNDO_AND_ERROR;
 
459
                                        }
 
460
                                        break;
 
461
                                }
 
462
                        case KX_PYATTRIBUTE_TYPE_ENUM:
 
463
                                // enum are equivalent to int, just make sure that the field size matches:
 
464
                                if (sizeof(int) != attrdef->m_size)
 
465
                                {
 
466
                                        PyErr_Format(PyExc_AttributeError, "Size check error for attribute, \"%s\", report to blender.org", attrdef->m_name);
 
467
                                        goto UNDO_AND_ERROR;
 
468
                                }
 
469
                                // walkthrough
 
470
                        case KX_PYATTRIBUTE_TYPE_INT:
 
471
                                {
 
472
                                        int *var = reinterpret_cast<int*>(ptr);
 
473
                                        ptr += sizeof(int);
 
474
                                        if (PyInt_Check(item)) 
 
475
                                        {
 
476
                                                long val = PyInt_AsLong(item);
 
477
                                                if (attrdef->m_clamp)
 
478
                                                {
 
479
                                                        if (val < attrdef->m_imin)
 
480
                                                                val = attrdef->m_imin;
 
481
                                                        else if (val > attrdef->m_imax)
 
482
                                                                val = attrdef->m_imax;
 
483
                                                }
 
484
                                                else if (val < attrdef->m_imin || val > attrdef->m_imax)
 
485
                                                {
 
486
                                                        PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
 
487
                                                        goto UNDO_AND_ERROR;
 
488
                                                }
 
489
                                                *var = (int)val;
 
490
                                        }
 
491
                                        else
 
492
                                        {
 
493
                                                PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
 
494
                                                goto UNDO_AND_ERROR;
 
495
                                        }
 
496
                                        break;
 
497
                                }
 
498
                        case KX_PYATTRIBUTE_TYPE_FLOAT:
 
499
                                {
 
500
                                        float *var = reinterpret_cast<float*>(ptr);
 
501
                                        ptr += sizeof(float);
 
502
                                        double val = PyFloat_AsDouble(item);
 
503
                                        if (val == -1.0 && PyErr_Occurred())
 
504
                                        {
 
505
                                                PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
 
506
                                                goto UNDO_AND_ERROR;
 
507
                                        }
 
508
                                        else if (attrdef->m_clamp) 
 
509
                                        {
 
510
                                                if (val < attrdef->m_fmin)
 
511
                                                        val = attrdef->m_fmin;
 
512
                                                else if (val > attrdef->m_fmax)
 
513
                                                        val = attrdef->m_fmax;
 
514
                                        }
 
515
                                        else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
 
516
                                        {
 
517
                                                PyErr_Format(PyExc_ValueError, "item value out of range for attribute \"%s\"", attrdef->m_name);
 
518
                                                goto UNDO_AND_ERROR;
 
519
                                        }
 
520
                                        *var = (float)val;
 
521
                                        break;
 
522
                                }
 
523
                        default:
 
524
                                // should not happen
 
525
                                PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", report to blender.org", attrdef->m_name);
 
526
                                goto UNDO_AND_ERROR;
 
527
                        }
 
528
                }
 
529
                // no error, call check function if any
 
530
                if (attrdef->m_checkFunction != NULL)
 
531
                {
 
532
                        if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
 
533
                        {
 
534
                                // if the checing function didnt set an error then set a generic one here so we dont set an error with no exception
 
535
                                if (PyErr_Occurred()==0)
 
536
                                        PyErr_Format(PyExc_AttributeError, "type check error for attribute \"%s\", reasion unknown", attrdef->m_name);
 
537
                                
 
538
                                // post check returned an error, restore values
 
539
                        UNDO_AND_ERROR:
 
540
                                if (undoBuffer)
 
541
                                {
 
542
                                        memcpy(sourceBuffer, undoBuffer, bufferSize);
 
543
                                        free(undoBuffer);
 
544
                                }
 
545
                                return PY_SET_ATTR_FAIL;
 
546
                        }
 
547
                }
 
548
                if (undoBuffer)
 
549
                        free(undoBuffer);
 
550
                return PY_SET_ATTR_SUCCESS;
 
551
        }
 
552
        else    // simple attribute value
 
553
        {
 
554
                if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_FUNCTION)
 
555
                {
 
556
                        if (attrdef->m_setFunction == NULL)
 
557
                        {
 
558
                                PyErr_Format(PyExc_AttributeError, "function attribute without function \"%s\", report to blender.org", attrdef->m_name);
 
559
                                return PY_SET_ATTR_FAIL;
 
560
                        }
 
561
                        return (*attrdef->m_setFunction)(self, attrdef, value);
 
562
                }
 
563
                if (attrdef->m_checkFunction != NULL || attrdef->m_type == KX_PYATTRIBUTE_TYPE_VECTOR)
 
564
                {
 
565
                        // post check function is provided, prepare undo buffer
 
566
                        sourceBuffer = ptr;
 
567
                        switch (attrdef->m_type) 
 
568
                        {
 
569
                        case KX_PYATTRIBUTE_TYPE_BOOL:
 
570
                                bufferSize = sizeof(bool);
 
571
                                break;
 
572
                        case KX_PYATTRIBUTE_TYPE_SHORT:
 
573
                                bufferSize = sizeof(short);
 
574
                                break;
 
575
                        case KX_PYATTRIBUTE_TYPE_ENUM:
 
576
                        case KX_PYATTRIBUTE_TYPE_INT:
 
577
                                bufferSize = sizeof(int);
 
578
                                break;
 
579
                        case KX_PYATTRIBUTE_TYPE_FLOAT:
 
580
                                bufferSize = sizeof(float);
 
581
                                break;
 
582
                        case KX_PYATTRIBUTE_TYPE_STRING:
 
583
                                sourceBuffer = reinterpret_cast<STR_String*>(ptr)->Ptr();
 
584
                                if (sourceBuffer)
 
585
                                        bufferSize = strlen(reinterpret_cast<char*>(sourceBuffer))+1;
 
586
                                break;
 
587
                        case KX_PYATTRIBUTE_TYPE_VECTOR:
 
588
                                bufferSize = sizeof(MT_Vector3);
 
589
                                break;
 
590
                        default:
 
591
                                PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
 
592
                                return PY_SET_ATTR_FAIL;
 
593
                        }
 
594
                        if (bufferSize)
 
595
                        {
 
596
                                undoBuffer = malloc(bufferSize);
 
597
                                if (undoBuffer)
 
598
                                {
 
599
                                        memcpy(undoBuffer, sourceBuffer, bufferSize);
 
600
                                }
 
601
                        }
 
602
                }
 
603
                        
 
604
                switch (attrdef->m_type) 
 
605
                {
 
606
                case KX_PYATTRIBUTE_TYPE_BOOL:
 
607
                        {
 
608
                                bool *var = reinterpret_cast<bool*>(ptr);
 
609
                                if (PyInt_Check(value)) 
 
610
                                {
 
611
                                        *var = (PyInt_AsLong(value) != 0);
 
612
                                } 
 
613
                                else if (PyBool_Check(value))
 
614
                                {
 
615
                                        *var = (value == Py_True);
 
616
                                }
 
617
                                else
 
618
                                {
 
619
                                        PyErr_Format(PyExc_TypeError, "expected an integer or a bool for attribute \"%s\"", attrdef->m_name);
 
620
                                        goto FREE_AND_ERROR;
 
621
                                }
 
622
                                break;
 
623
                        }
 
624
                case KX_PYATTRIBUTE_TYPE_SHORT:
 
625
                        {
 
626
                                short int *var = reinterpret_cast<short int*>(ptr);
 
627
                                if (PyInt_Check(value)) 
 
628
                                {
 
629
                                        long val = PyInt_AsLong(value);
 
630
                                        if (attrdef->m_clamp)
 
631
                                        {
 
632
                                                if (val < attrdef->m_imin)
 
633
                                                        val = attrdef->m_imin;
 
634
                                                else if (val > attrdef->m_imax)
 
635
                                                        val = attrdef->m_imax;
 
636
                                        }
 
637
                                        else if (val < attrdef->m_imin || val > attrdef->m_imax)
 
638
                                        {
 
639
                                                PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
 
640
                                                goto FREE_AND_ERROR;
 
641
                                        }
 
642
                                        *var = (short int)val;
 
643
                                }
 
644
                                else
 
645
                                {
 
646
                                        PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
 
647
                                        goto FREE_AND_ERROR;
 
648
                                }
 
649
                                break;
 
650
                        }
 
651
                case KX_PYATTRIBUTE_TYPE_ENUM:
 
652
                        // enum are equivalent to int, just make sure that the field size matches:
 
653
                        if (sizeof(int) != attrdef->m_size)
 
654
                        {
 
655
                                PyErr_Format(PyExc_AttributeError, "attribute size check error for attribute \"%s\", report to blender.org", attrdef->m_name);
 
656
                                goto FREE_AND_ERROR;
 
657
                        }
 
658
                        // walkthrough
 
659
                case KX_PYATTRIBUTE_TYPE_INT:
 
660
                        {
 
661
                                int *var = reinterpret_cast<int*>(ptr);
 
662
                                if (PyInt_Check(value)) 
 
663
                                {
 
664
                                        long val = PyInt_AsLong(value);
 
665
                                        if (attrdef->m_clamp)
 
666
                                        {
 
667
                                                if (val < attrdef->m_imin)
 
668
                                                        val = attrdef->m_imin;
 
669
                                                else if (val > attrdef->m_imax)
 
670
                                                        val = attrdef->m_imax;
 
671
                                        }
 
672
                                        else if (val < attrdef->m_imin || val > attrdef->m_imax)
 
673
                                        {
 
674
                                                PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
 
675
                                                goto FREE_AND_ERROR;
 
676
                                        }
 
677
                                        *var = (int)val;
 
678
                                }
 
679
                                else
 
680
                                {
 
681
                                        PyErr_Format(PyExc_TypeError, "expected an integer for attribute \"%s\"", attrdef->m_name);
 
682
                                        goto FREE_AND_ERROR;
 
683
                                }
 
684
                                break;
 
685
                        }
 
686
                case KX_PYATTRIBUTE_TYPE_FLOAT:
 
687
                        {
 
688
                                float *var = reinterpret_cast<float*>(ptr);
 
689
                                double val = PyFloat_AsDouble(value);
 
690
                                if (val == -1.0 && PyErr_Occurred())
 
691
                                {
 
692
                                        PyErr_Format(PyExc_TypeError, "expected a float for attribute \"%s\"", attrdef->m_name);
 
693
                                        goto FREE_AND_ERROR;
 
694
                                }
 
695
                                else if (attrdef->m_clamp)
 
696
                                {
 
697
                                        if (val < attrdef->m_fmin)
 
698
                                                val = attrdef->m_fmin;
 
699
                                        else if (val > attrdef->m_fmax)
 
700
                                                val = attrdef->m_fmax;
 
701
                                }
 
702
                                else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
 
703
                                {
 
704
                                        PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
 
705
                                        goto FREE_AND_ERROR;
 
706
                                }
 
707
                                *var = (float)val;
 
708
                                break;
 
709
                        }
 
710
                case KX_PYATTRIBUTE_TYPE_VECTOR:
 
711
                        {
 
712
                                if (!PySequence_Check(value) || PySequence_Size(value) != 3) 
 
713
                                {
 
714
                                        PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
 
715
                                        return PY_SET_ATTR_FAIL;
 
716
                                }
 
717
                                MT_Vector3 *var = reinterpret_cast<MT_Vector3*>(ptr);
 
718
                                for (int i=0; i<3; i++)
 
719
                                {
 
720
                                        PyObject *item = PySequence_GetItem(value, i); /* new ref */
 
721
                                        // we can decrement the reference immediately, the reference count
 
722
                                        // is at least 1 because the item is part of an array
 
723
                                        Py_DECREF(item);
 
724
                                        double val = PyFloat_AsDouble(item);
 
725
                                        if (val == -1.0 && PyErr_Occurred())
 
726
                                        {
 
727
                                                PyErr_Format(PyExc_TypeError, "expected a sequence of 3 floats for attribute \"%s\"", attrdef->m_name);
 
728
                                                goto RESTORE_AND_ERROR;
 
729
                                        }
 
730
                                        else if (attrdef->m_clamp)
 
731
                                        {
 
732
                                                if (val < attrdef->m_fmin)
 
733
                                                        val = attrdef->m_fmin;
 
734
                                                else if (val > attrdef->m_fmax)
 
735
                                                        val = attrdef->m_fmax;
 
736
                                        }
 
737
                                        else if (val < attrdef->m_fmin || val > attrdef->m_fmax)
 
738
                                        {
 
739
                                                PyErr_Format(PyExc_ValueError, "value out of range for attribute \"%s\"", attrdef->m_name);
 
740
                                                goto RESTORE_AND_ERROR;
 
741
                                        }
 
742
                                        (*var)[i] = (MT_Scalar)val;
 
743
                                }
 
744
                                break;
 
745
                        }
 
746
                case KX_PYATTRIBUTE_TYPE_STRING:
 
747
                        {
 
748
                                STR_String *var = reinterpret_cast<STR_String*>(ptr);
 
749
                                if (PyString_Check(value)) 
 
750
                                {
 
751
                                        char *val = PyString_AsString(value);
 
752
                                        if (attrdef->m_clamp)
 
753
                                        {
 
754
                                                if (strlen(val) < attrdef->m_imin)
 
755
                                                {
 
756
                                                        // can't increase the length of the string
 
757
                                                        PyErr_Format(PyExc_ValueError, "string length too short for attribute \"%s\"", attrdef->m_name);
 
758
                                                        goto FREE_AND_ERROR;
 
759
                                                }
 
760
                                                else if (strlen(val) > attrdef->m_imax)
 
761
                                                {
 
762
                                                        // trim the string
 
763
                                                        char c = val[attrdef->m_imax];
 
764
                                                        val[attrdef->m_imax] = 0;
 
765
                                                        *var = val;
 
766
                                                        val[attrdef->m_imax] = c;
 
767
                                                        break;
 
768
                                                }
 
769
                                        } else if (strlen(val) < attrdef->m_imin || strlen(val) > attrdef->m_imax)
 
770
                                        {
 
771
                                                PyErr_Format(PyExc_ValueError, "string length out of range for attribute \"%s\"", attrdef->m_name);
 
772
                                                goto FREE_AND_ERROR;
 
773
                                        }
 
774
                                        *var = val;
 
775
                                }
 
776
                                else
 
777
                                {
 
778
                                        PyErr_Format(PyExc_TypeError, "expected a string for attribute \"%s\"", attrdef->m_name);
 
779
                                        goto FREE_AND_ERROR;
 
780
                                }
 
781
                                break;
 
782
                        }
 
783
                default:
 
784
                        // should not happen
 
785
                        PyErr_Format(PyExc_AttributeError, "unknown type for attribute \"%s\", report to blender.org", attrdef->m_name);
 
786
                        goto FREE_AND_ERROR;
 
787
                }
 
788
        }
 
789
        // check if post processing is needed
 
790
        if (attrdef->m_checkFunction != NULL)
 
791
        {
 
792
                if ((*attrdef->m_checkFunction)(self, attrdef) != 0)
 
793
                {
 
794
                        // restore value
 
795
                RESTORE_AND_ERROR:
 
796
                        if (undoBuffer)
 
797
                        {
 
798
                                if (attrdef->m_type == KX_PYATTRIBUTE_TYPE_STRING)
 
799
                                {
 
800
                                        // special case for STR_String: restore the string
 
801
                                        STR_String *var = reinterpret_cast<STR_String*>(ptr);
 
802
                                        *var = reinterpret_cast<char*>(undoBuffer);
 
803
                                }
 
804
                                else
 
805
                                {
 
806
                                        // other field type have direct values
 
807
                                        memcpy(ptr, undoBuffer, bufferSize);
 
808
                                }
 
809
                        }
 
810
                FREE_AND_ERROR:
 
811
                        if (undoBuffer)
 
812
                                free(undoBuffer);
 
813
                        return 1;
 
814
                }
 
815
        }
 
816
        if (undoBuffer)
 
817
                free(undoBuffer);
 
818
        return 0;       
 
819
}
 
820
 
 
821
 
133
822
 
134
823
/*------------------------------
135
824
 * PyObjectPlus repr            -- representations
136
825
------------------------------*/
137
 
PyObject *PyObjectPlus::_repr(void)
 
826
PyObject *PyObjectPlus::py_repr(void)
138
827
{
139
828
        PyErr_SetString(PyExc_SystemError, "Representation not overridden by object.");  
140
829
        return NULL;
145
834
------------------------------*/
146
835
bool PyObjectPlus::isA(PyTypeObject *T)         // if called with a Type, use "typename"
147
836
{
148
 
  return isA(T->tp_name);
 
837
        int i;
 
838
        PyParentObject  P;
 
839
        PyParentObject *Ps = GetParents();
 
840
 
 
841
        for (P = Ps[i=0]; P != NULL; P = Ps[i++])
 
842
                if (P==T)
 
843
                        return true;
 
844
 
 
845
        return false;
149
846
}
150
847
 
151
848
 
152
849
bool PyObjectPlus::isA(const char *mytypename)          // check typename of each parent
153
850
{
154
 
  int i;
155
 
  PyParentObject  P;
156
 
  PyParentObject *Ps = GetParents();
 
851
        int i;
 
852
        PyParentObject  P;
 
853
        PyParentObject *Ps = GetParents();
157
854
  
158
 
  for (P = Ps[i=0]; P != NULL; P = Ps[i++])
159
 
  {
160
 
      if (STR_String(P->tp_name) == STR_String(mytypename)      )
161
 
                  return true;
162
 
  }
163
 
        
164
 
  return false;
165
 
}
166
 
 
167
 
PyObject *PyObjectPlus::Py_isA(PyObject *args)          // Python wrapper for isA
168
 
{
169
 
  char *mytypename;
170
 
  if (!PyArg_ParseTuple(args, "s", &mytypename))
171
 
    return NULL;
172
 
  if(isA(mytypename))
173
 
    Py_RETURN_TRUE;
174
 
  else
175
 
    Py_RETURN_FALSE;
176
 
}
 
855
        for (P = Ps[i=0]; P != NULL; P = Ps[i++])
 
856
                if (strcmp(P->tp_name, mytypename)==0)
 
857
                        return true;
 
858
 
 
859
        return false;
 
860
}
 
861
 
 
862
PyObject *PyObjectPlus::PyisA(PyObject *value)          // Python wrapper for isA
 
863
{
 
864
        if (PyType_Check(value)) {
 
865
                return PyBool_FromLong(isA((PyTypeObject *)value));
 
866
        } else if (PyString_Check(value)) {
 
867
                return PyBool_FromLong(isA(PyString_AsString(value)));
 
868
        }
 
869
    PyErr_SetString(PyExc_TypeError, "object.isA(value): expected a type or a string");
 
870
    return NULL;        
 
871
}
 
872
 
 
873
 
 
874
void PyObjectPlus::ProcessReplica()
 
875
{
 
876
        /* Clear the proxy, will be created again if needed with GetProxy()
 
877
         * otherwise the PyObject will point to the wrong reference */
 
878
        m_proxy= NULL;
 
879
}
 
880
 
 
881
/* Sometimes we might want to manually invalidate a BGE type even if
 
882
 * it hasnt been released by the BGE, say for example when an object
 
883
 * is removed from a scene, accessing it may cause problems.
 
884
 * 
 
885
 * In this case the current proxy is made invalid, disowned,
 
886
 * and will raise an error on access. However if python can get access
 
887
 * to this class again it will make a new proxy and work as expected.
 
888
 */
 
889
void PyObjectPlus::InvalidateProxy()            // check typename of each parent
 
890
{
 
891
        if(m_proxy) { 
 
892
                BGE_PROXY_REF(m_proxy)=NULL;
 
893
                Py_DECREF(m_proxy);
 
894
                m_proxy= NULL;
 
895
        }
 
896
}
 
897
 
 
898
/* Utility function called by the macro py_getattro_up()
 
899
 * for getting ob.__dict__() values from our PyObject
 
900
 * this is used by python for doing dir() on an object, so its good
 
901
 * if we return a list of attributes and methods.
 
902
 * 
 
903
 * Other then making dir() useful the value returned from __dict__() is not useful
 
904
 * since every value is a Py_None
 
905
 * */
 
906
PyObject *py_getattr_dict(PyObject *pydict, PyObject *tp_dict)
 
907
{
 
908
    if(pydict==NULL) { /* incase calling __dict__ on the parent of this object raised an error */
 
909
        PyErr_Clear();
 
910
        pydict = PyDict_New();
 
911
    }
 
912
        
 
913
        PyDict_Update(pydict, tp_dict);
 
914
        return pydict;
 
915
}
 
916
 
 
917
 
 
918
 
 
919
PyObject *PyObjectPlus::GetProxy_Ext(PyObjectPlus *self, PyTypeObject *tp)
 
920
{
 
921
        if (self->m_proxy==NULL)
 
922
        {
 
923
                self->m_proxy = reinterpret_cast<PyObject *>PyObject_NEW( PyObjectPlus_Proxy, tp);
 
924
                BGE_PROXY_PYOWNS(self->m_proxy) = false;
 
925
        }
 
926
        //PyObject_Print(self->m_proxy, stdout, 0);
 
927
        //printf("ref %d\n", self->m_proxy->ob_refcnt);
 
928
        
 
929
        BGE_PROXY_REF(self->m_proxy) = self; /* Its possible this was set to NULL, so set it back here */
 
930
        Py_INCREF(self->m_proxy); /* we own one, thos ones fore the return */
 
931
        return self->m_proxy;
 
932
}
 
933
 
 
934
PyObject *PyObjectPlus::NewProxy_Ext(PyObjectPlus *self, PyTypeObject *tp, bool py_owns)
 
935
{
 
936
        if (self->m_proxy)
 
937
        {
 
938
                if(py_owns)
 
939
                {       /* Free */
 
940
                        BGE_PROXY_REF(self->m_proxy) = NULL;
 
941
                        Py_DECREF(self->m_proxy);
 
942
                        self->m_proxy= NULL;
 
943
                }
 
944
                else {
 
945
                        Py_INCREF(self->m_proxy);
 
946
                        return self->m_proxy;
 
947
                }
 
948
                
 
949
        }
 
950
        
 
951
        GetProxy_Ext(self, tp);
 
952
        if(py_owns) {
 
953
                BGE_PROXY_PYOWNS(self->m_proxy) = py_owns;
 
954
                Py_DECREF(self->m_proxy); /* could avoid thrashing here but for now its ok */
 
955
        }
 
956
        return self->m_proxy;
 
957
}
 
958
 
 
959
///////////////////////////////////////////////////////////////////////////////////////////////
 
960
///////////////////////////////////////////////////////////////////////////////////////////////
 
961
/* deprecation warning management */
 
962
 
 
963
bool PyObjectPlus::m_ignore_deprecation_warnings(false);
 
964
void PyObjectPlus::SetDeprecationWarnings(bool ignoreDeprecationWarnings)
 
965
{
 
966
        m_ignore_deprecation_warnings = ignoreDeprecationWarnings;
 
967
}
 
968
 
 
969
void PyObjectPlus::ShowDeprecationWarning_func(const char* old_way,const char* new_way)
 
970
{
 
971
        {
 
972
                printf("Method %s is deprecated, please use %s instead.\n", old_way, new_way);
 
973
                
 
974
                // import sys; print '\t%s:%d' % (sys._getframe(0).f_code.co_filename, sys._getframe(0).f_lineno)
 
975
                
 
976
                PyObject *getframe, *frame;
 
977
                PyObject *f_lineno, *f_code, *co_filename;
 
978
                
 
979
                getframe = PySys_GetObject((char *)"_getframe"); // borrowed
 
980
                if (getframe) {
 
981
                        frame = PyObject_CallObject(getframe, NULL);
 
982
                        if (frame) {
 
983
                                f_lineno= PyObject_GetAttrString(frame, "f_lineno");
 
984
                                f_code= PyObject_GetAttrString(frame, "f_code");
 
985
                                if (f_lineno && f_code) {
 
986
                                        co_filename= PyObject_GetAttrString(f_code, "co_filename");
 
987
                                        if (co_filename) {
 
988
                                                
 
989
                                                printf("\t%s:%d\n", PyString_AsString(co_filename), (int)PyInt_AsLong(f_lineno));
 
990
                                                
 
991
                                                Py_DECREF(f_lineno);
 
992
                                                Py_DECREF(f_code);
 
993
                                                Py_DECREF(co_filename);
 
994
                                                Py_DECREF(frame);
 
995
                                                return;
 
996
                                        }
 
997
                                }
 
998
                                
 
999
                                Py_XDECREF(f_lineno);
 
1000
                                Py_XDECREF(f_code);
 
1001
                                Py_DECREF(frame);
 
1002
                        }
 
1003
                        
 
1004
                }
 
1005
                PyErr_Clear();
 
1006
                printf("\tERROR - Could not access sys._getframe(0).f_lineno or sys._getframe().f_code.co_filename\n");
 
1007
        }
 
1008
}
 
1009
 
 
1010
void PyObjectPlus::ClearDeprecationWarning()
 
1011
{
 
1012
        WarnLink *wlink_next;
 
1013
        WarnLink *wlink = GetDeprecationWarningLinkFirst();
 
1014
        
 
1015
        while(wlink)
 
1016
        {
 
1017
                wlink->warn_done= false; /* no need to NULL the link, its cleared before adding to the list next time round */
 
1018
                wlink_next= reinterpret_cast<WarnLink *>(wlink->link);
 
1019
                wlink->link= NULL;
 
1020
                wlink= wlink_next;
 
1021
        }
 
1022
        NullDeprecationWarning();
 
1023
}
 
1024
 
 
1025
WarnLink*               m_base_wlink_first= NULL;
 
1026
WarnLink*               m_base_wlink_last= NULL;
 
1027
 
 
1028
WarnLink*               PyObjectPlus::GetDeprecationWarningLinkFirst(void) {return m_base_wlink_first;}
 
1029
WarnLink*               PyObjectPlus::GetDeprecationWarningLinkLast(void) {return m_base_wlink_last;}
 
1030
void                    PyObjectPlus::SetDeprecationWarningFirst(WarnLink* wlink) {m_base_wlink_first= wlink;}
 
1031
void                    PyObjectPlus::SetDeprecationWarningLinkLast(WarnLink* wlink) {m_base_wlink_last= wlink;}
 
1032
void                    PyObjectPlus::NullDeprecationWarning() {m_base_wlink_first= m_base_wlink_last= NULL;}
177
1033
 
178
1034
#endif //NO_EXP_PYTHON_EMBEDDING
179
1035