##// END OF EJS Templates
added support for std::exception handling...
florianlink -
r159:aaeb49c23826
parent child
Show More
@@ -1,526 +1,556
1 1 /*
2 2 *
3 3 * Copyright (C) 2010 MeVis Medical Solutions AG All Rights Reserved.
4 4 *
5 5 * This library is free software; you can redistribute it and/or
6 6 * modify it under the terms of the GNU Lesser General Public
7 7 * License as published by the Free Software Foundation; either
8 8 * version 2.1 of the License, or (at your option) any later version.
9 9 *
10 10 * This library is distributed in the hope that it will be useful,
11 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 13 * Lesser General Public License for more details.
14 14 *
15 15 * Further, this software is distributed without any warranty that it is
16 16 * free of the rightful claim of any third person regarding infringement
17 17 * or the like. Any license provided herein, whether implied or
18 18 * otherwise, applies only to this software file. Patent licenses, if
19 19 * any, provided herein do not apply to combinations of this program with
20 20 * other software, or any other product whatsoever.
21 21 *
22 22 * You should have received a copy of the GNU Lesser General Public
23 23 * License along with this library; if not, write to the Free Software
24 24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 25 *
26 26 * Contact information: MeVis Medical Solutions AG, Universitaetsallee 29,
27 27 * 28359 Bremen, Germany or:
28 28 *
29 29 * http://www.mevis.de
30 30 *
31 31 */
32 32
33 33 //----------------------------------------------------------------------------------
34 34 /*!
35 35 // \file PythonQtSlot.cpp
36 36 // \author Florian Link
37 37 // \author Last changed by $Author: florian $
38 38 // \date 2006-05
39 39 */
40 40 //----------------------------------------------------------------------------------
41 41
42 42 #include "PythonQt.h"
43 43 #include "PythonQtSlot.h"
44 44 #include "PythonQtInstanceWrapper.h"
45 45 #include "PythonQtClassInfo.h"
46 46 #include "PythonQtMisc.h"
47 47 #include "PythonQtConversion.h"
48 48 #include <iostream>
49 49
50 #include <exception>
51 #include <stdexcept>
52
50 53 #define PYTHONQT_MAX_ARGS 32
51 54
52 55
53 56 bool PythonQtCallSlot(PythonQtClassInfo* classInfo, QObject* objectToCall, PyObject* args, bool strict, PythonQtSlotInfo* info, void* firstArgument, PyObject** pythonReturnValue, void** directReturnValuePointer)
54 57 {
55 58 static unsigned int recursiveEntry = 0;
56 59
57 60 if (directReturnValuePointer) {
58 61 *directReturnValuePointer = NULL;
59 62 }
60 63 // store the current storage position, so that we can get back to this state after a slot is called
61 64 // (do this locally, so that we have all positions on the stack
62 65 PythonQtValueStoragePosition globalValueStoragePos;
63 66 PythonQtValueStoragePosition globalPtrStoragePos;
64 67 PythonQtValueStoragePosition globalVariantStoragePos;
65 68 PythonQtConv::global_valueStorage.getPos(globalValueStoragePos);
66 69 PythonQtConv::global_ptrStorage.getPos(globalPtrStoragePos);
67 70 PythonQtConv::global_variantStorage.getPos(globalVariantStoragePos);
68 71
69 72 recursiveEntry++;
70 73
71 74 // the arguments that are passed to qt_metacall
72 75 void* argList[PYTHONQT_MAX_ARGS];
73 76 PyObject* result = NULL;
74 77 int argc = info->parameterCount();
75 78 const QList<PythonQtSlotInfo::ParameterInfo>& params = info->parameters();
76 79
77 80 const PythonQtSlotInfo::ParameterInfo& returnValueParam = params.at(0);
78 81 // set return argument to NULL
79 82 argList[0] = NULL;
80 83
81 84 bool ok = true;
82 85 bool skipFirst = false;
83 86 if (info->isInstanceDecorator()) {
84 87 skipFirst = true;
85 88
86 89 // for decorators on CPP objects, we take the cpp ptr, for QObjects we take the QObject pointer
87 90 void* arg1 = firstArgument;
88 91 if (!arg1) {
89 92 arg1 = objectToCall;
90 93 }
91 94 if (arg1) {
92 95 // upcast to correct parent class
93 96 arg1 = ((char*)arg1)+info->upcastingOffset();
94 97 }
95 98
96 99 argList[1] = &arg1;
97 100 if (ok) {
98 101 for (int i = 2; i<argc && ok; i++) {
99 102 const PythonQtSlotInfo::ParameterInfo& param = params.at(i);
100 103 argList[i] = PythonQtConv::ConvertPythonToQt(param, PyTuple_GET_ITEM(args, i-2), strict, classInfo);
101 104 if (argList[i]==NULL) {
102 105 ok = false;
103 106 break;
104 107 }
105 108 }
106 109 }
107 110 } else {
108 111 for (int i = 1; i<argc && ok; i++) {
109 112 const PythonQtSlotInfo::ParameterInfo& param = params.at(i);
110 113 argList[i] = PythonQtConv::ConvertPythonToQt(param, PyTuple_GET_ITEM(args, i-1), strict, classInfo);
111 114 if (argList[i]==NULL) {
112 115 ok = false;
113 116 break;
114 117 }
115 118 }
116 119 }
117 120
118 121 if (ok) {
119 122 // parameters are ok, now create the qt return value which is assigned to by metacall
120 123 if (returnValueParam.typeId != QMetaType::Void) {
121 124 // create empty default value for the return value
122 125 if (!directReturnValuePointer) {
123 126 // create empty default value for the return value
124 127 argList[0] = PythonQtConv::CreateQtReturnValue(returnValueParam);
125 128 if (argList[0]==NULL) {
126 129 // return value could not be created, maybe we have a registered class with a default constructor, so that we can construct the pythonqt wrapper object and
127 130 // pass its internal pointer
128 131 PythonQtClassInfo* info = PythonQt::priv()->getClassInfo(returnValueParam.name);
129 132 if (info && info->pythonQtClassWrapper()) {
130 133 PyObject* emptyTuple = PyTuple_New(0);
131 134 // 1) default construct an empty object as python object (owned by PythonQt), by calling the meta class with empty arguments
132 135 result = PyObject_Call((PyObject*)info->pythonQtClassWrapper(), emptyTuple, NULL);
133 136 if (result) {
134 137 argList[0] = ((PythonQtInstanceWrapper*)result)->_wrappedPtr;
135 138 }
136 139 Py_DECREF(emptyTuple);
137 140 }
138 141 }
139 142 } else {
140 143 // we can use our pointer directly!
141 144 argList[0] = directReturnValuePointer;
142 145 }
143 146 }
144 147
145 148
146 149 PythonQt::ProfilingCB* profilingCB = PythonQt::priv()->profilingCB();
147 150 if (profilingCB) {
148 151 const char* className = NULL;
149 152 if (info->decorator()) {
150 153 className = info->decorator()->metaObject()->className();
151 154 } else {
152 155 className = objectToCall->metaObject()->className();
153 156 }
154 157
155 158 profilingCB(PythonQt::Enter, className, info->metaMethod()->signature());
156 159 }
157 160
158 161 // invoke the slot via metacall
162 bool hadException = false;
163 try {
159 164 (info->decorator()?info->decorator():objectToCall)->qt_metacall(QMetaObject::InvokeMetaMethod, info->slotIndex(), argList);
165 } catch (std::bad_alloc & e) {
166 hadException = true;
167 QByteArray what("std::bad_alloc: ");
168 what += e.what();
169 PyErr_SetString(PyExc_MemoryError, what.constData());
170 } catch (std::runtime_error & e) {
171 hadException = true;
172 QByteArray what("std::runtime_error: ");
173 what += e.what();
174 PyErr_SetString(PyExc_RuntimeError, what.constData());
175 } catch (std::logic_error & e) {
176 hadException = true;
177 QByteArray what("std::logic_error: ");
178 what += e.what();
179 PyErr_SetString(PyExc_RuntimeError, what.constData());
180 } catch (std::exception& e) {
181 hadException = true;
182 QByteArray what("std::exception: ");
183 what += e.what();
184 PyErr_SetString(PyExc_StandardError, what.constData());
185 }
160 186
161 187 if (profilingCB) {
162 188 profilingCB(PythonQt::Leave, NULL, NULL);
163 189 }
164 190
165 191 // handle the return value (which in most cases still needs to be converted to a Python object)
192 if (!hadException) {
166 193 if (argList[0] || returnValueParam.typeId == QMetaType::Void) {
167 194 if (directReturnValuePointer) {
168 195 result = NULL;
169 196 } else {
170 197 // the resulting object maybe present already, because we created it above at 1)...
171 198 if (!result) {
172 199 result = PythonQtConv::ConvertQtValueToPython(returnValueParam, argList[0]);
173 200 }
174 201 }
175 202 } else {
176 203 QString e = QString("Called ") + info->fullSignature() + ", return type '" + returnValueParam.name + "' is ignored because it is unknown to PythonQt. Probably you should register it using qRegisterMetaType() or add a default constructor decorator to the class.";
177 204 PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
178 205 result = NULL;
179 206 }
207 } else {
208 result = NULL;
209 }
180 210 }
181 211 recursiveEntry--;
182 212
183 213 // reset the parameter storage position to the stored pos to "pop" the parameter stack
184 214 PythonQtConv::global_valueStorage.setPos(globalValueStoragePos);
185 215 PythonQtConv::global_ptrStorage.setPos(globalPtrStoragePos);
186 216 PythonQtConv::global_variantStorage.setPos(globalVariantStoragePos);
187 217
188 218 *pythonReturnValue = result;
189 219 // NOTE: it is important to only return here, otherwise the stack will not be popped!!!
190 220 return result || (directReturnValuePointer && *directReturnValuePointer);
191 221 }
192 222
193 223 //-----------------------------------------------------------------------------------
194 224
195 225 static PythonQtSlotFunctionObject *pythonqtslot_free_list = NULL;
196 226
197 227 PyObject *PythonQtSlotFunction_Call(PyObject *func, PyObject *args, PyObject *kw)
198 228 {
199 229 PythonQtSlotFunctionObject* f = (PythonQtSlotFunctionObject*)func;
200 230 PythonQtSlotInfo* info = f->m_ml;
201 231 if (PyObject_TypeCheck(f->m_self, &PythonQtInstanceWrapper_Type)) {
202 232 PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*) f->m_self;
203 233 if (!info->isClassDecorator() && (self->_obj==NULL && self->_wrappedPtr==NULL)) {
204 234 QString error = QString("Trying to call '") + f->m_ml->slotName() + "' on a destroyed " + self->classInfo()->className() + " object";
205 235 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
206 236 return NULL;
207 237 } else {
208 238 return PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, args, kw, self->_wrappedPtr);
209 239 }
210 240 } else if (f->m_self->ob_type == &PythonQtClassWrapper_Type) {
211 241 PythonQtClassWrapper* type = (PythonQtClassWrapper*) f->m_self;
212 242 if (info->isClassDecorator()) {
213 243 return PythonQtSlotFunction_CallImpl(type->classInfo(), NULL, info, args, kw);
214 244 } else {
215 245 // otherwise, it is an unbound call and we have an instanceDecorator or normal slot...
216 246 Py_ssize_t argc = PyTuple_Size(args);
217 247 if (argc>0) {
218 248 PyObject* firstArg = PyTuple_GET_ITEM(args, 0);
219 249 if (PyObject_TypeCheck(firstArg, (PyTypeObject*)&PythonQtInstanceWrapper_Type)
220 250 && ((PythonQtInstanceWrapper*)firstArg)->classInfo()->inherits(type->classInfo())) {
221 251 PythonQtInstanceWrapper* self = (PythonQtInstanceWrapper*)firstArg;
222 252 if (!info->isClassDecorator() && (self->_obj==NULL && self->_wrappedPtr==NULL)) {
223 253 QString error = QString("Trying to call '") + f->m_ml->slotName() + "' on a destroyed " + self->classInfo()->className() + " object";
224 254 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
225 255 return NULL;
226 256 }
227 257 // strip the first argument...
228 258 PyObject* newargs = PyTuple_GetSlice(args, 1, argc);
229 259 PyObject* result = PythonQtSlotFunction_CallImpl(self->classInfo(), self->_obj, info, newargs, kw, self->_wrappedPtr);
230 260 Py_DECREF(newargs);
231 261 return result;
232 262 } else {
233 263 // first arg is not of correct type!
234 264 QString error = "slot " + info->fullSignature() + " requires " + type->classInfo()->className() + " instance as first argument, got " + firstArg->ob_type->tp_name;
235 265 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
236 266 return NULL;
237 267 }
238 268 } else {
239 269 // wrong number of args
240 270 QString error = "slot " + info->fullSignature() + " requires " + type->classInfo()->className() + " instance as first argument.";
241 271 PyErr_SetString(PyExc_ValueError, error.toLatin1().data());
242 272 return NULL;
243 273 }
244 274 }
245 275 }
246 276 return NULL;
247 277 }
248 278
249 279 PyObject *PythonQtSlotFunction_CallImpl(PythonQtClassInfo* classInfo, QObject* objectToCall, PythonQtSlotInfo* info, PyObject *args, PyObject * /*kw*/, void* firstArg, void** directReturnValuePointer)
250 280 {
251 281 int argc = args?PyTuple_Size(args):0;
252 282
253 283 #ifdef PYTHONQT_DEBUG
254 284 std::cout << "called " << info->metaMethod()->typeName() << " " << info->metaMethod()->signature() << std::endl;
255 285 #endif
256 286
257 287 PyObject* r = NULL;
258 288 bool ok = false;
259 289 if (directReturnValuePointer) {
260 290 *directReturnValuePointer = NULL;
261 291 }
262 292 if (info->nextInfo()) {
263 293 // overloaded slot call, try on all slots with strict conversion first
264 294 bool strict = true;
265 295 PythonQtSlotInfo* i = info;
266 296 while (i) {
267 297 bool skipFirst = i->isInstanceDecorator();
268 298 if (i->parameterCount()-1-(skipFirst?1:0) == argc) {
269 299 PyErr_Clear();
270 300 ok = PythonQtCallSlot(classInfo, objectToCall, args, strict, i, firstArg, &r, directReturnValuePointer);
271 301 if (PyErr_Occurred() || ok) break;
272 302 }
273 303 i = i->nextInfo();
274 304 if (!i) {
275 305 if (strict) {
276 306 // one more run without being strict
277 307 strict = false;
278 308 i = info;
279 309 }
280 310 }
281 311 }
282 312 if (!ok && !PyErr_Occurred()) {
283 313 QString e = QString("Could not find matching overload for given arguments:\n" + PythonQtConv::PyObjGetString(args) + "\n The following slots are available:\n");
284 314 PythonQtSlotInfo* i = info;
285 315 while (i) {
286 316 e += QString(i->fullSignature()) + "\n";
287 317 i = i->nextInfo();
288 318 }
289 319 PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
290 320 }
291 321 } else {
292 322 // simple (non-overloaded) slot call
293 323 bool skipFirst = info->isInstanceDecorator();
294 324 if (info->parameterCount()-1-(skipFirst?1:0) == argc) {
295 325 PyErr_Clear();
296 326 ok = PythonQtCallSlot(classInfo, objectToCall, args, false, info, firstArg, &r, directReturnValuePointer);
297 327 if (!ok && !PyErr_Occurred()) {
298 328 QString e = QString("Called ") + info->fullSignature() + " with wrong arguments: " + PythonQtConv::PyObjGetString(args);
299 329 PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
300 330 }
301 331 } else {
302 332 QString e = QString("Called ") + info->fullSignature() + " with wrong number of arguments: " + PythonQtConv::PyObjGetString(args);
303 333 PyErr_SetString(PyExc_ValueError, e.toLatin1().data());
304 334 }
305 335 }
306 336
307 337 return r;
308 338 }
309 339
310 340 PyObject *
311 341 PythonQtSlotFunction_New(PythonQtSlotInfo *ml, PyObject *self, PyObject *module)
312 342 {
313 343 PythonQtSlotFunctionObject *op;
314 344 op = pythonqtslot_free_list;
315 345 if (op != NULL) {
316 346 pythonqtslot_free_list = (PythonQtSlotFunctionObject *)(op->m_self);
317 347 PyObject_INIT(op, &PythonQtSlotFunction_Type);
318 348 }
319 349 else {
320 350 op = PyObject_GC_New(PythonQtSlotFunctionObject, &PythonQtSlotFunction_Type);
321 351 if (op == NULL)
322 352 return NULL;
323 353 }
324 354 op->m_ml = ml;
325 355 Py_XINCREF(self);
326 356 op->m_self = self;
327 357 Py_XINCREF(module);
328 358 op->m_module = module;
329 359 PyObject_GC_Track(op);
330 360 return (PyObject *)op;
331 361 }
332 362
333 363 PythonQtSlotInfo*
334 364 PythonQtSlotFunction_GetSlotInfo(PyObject *op)
335 365 {
336 366 if (!PythonQtSlotFunction_Check(op)) {
337 367 PyErr_BadInternalCall();
338 368 return NULL;
339 369 }
340 370 return ((PythonQtSlotFunctionObject *)op) -> m_ml;
341 371 }
342 372
343 373 PyObject *
344 374 PythonQtSlotFunction_GetSelf(PyObject *op)
345 375 {
346 376 if (!PythonQtSlotFunction_Check(op)) {
347 377 PyErr_BadInternalCall();
348 378 return NULL;
349 379 }
350 380 return ((PythonQtSlotFunctionObject *)op) -> m_self;
351 381 }
352 382
353 383 /* Methods (the standard built-in methods, that is) */
354 384
355 385 static void
356 386 meth_dealloc(PythonQtSlotFunctionObject *m)
357 387 {
358 388 PyObject_GC_UnTrack(m);
359 389 Py_XDECREF(m->m_self);
360 390 Py_XDECREF(m->m_module);
361 391 m->m_self = (PyObject *)pythonqtslot_free_list;
362 392 pythonqtslot_free_list = m;
363 393 }
364 394
365 395 static PyObject *
366 396 meth_get__doc__(PythonQtSlotFunctionObject * /*m*/, void * /*closure*/)
367 397 {
368 398 Py_INCREF(Py_None);
369 399 return Py_None;
370 400 }
371 401
372 402 static PyObject *
373 403 meth_get__name__(PythonQtSlotFunctionObject *m, void * /*closure*/)
374 404 {
375 405 return PyString_FromString(m->m_ml->metaMethod()->signature());
376 406 }
377 407
378 408 static int
379 409 meth_traverse(PythonQtSlotFunctionObject *m, visitproc visit, void *arg)
380 410 {
381 411 int err;
382 412 if (m->m_self != NULL) {
383 413 err = visit(m->m_self, arg);
384 414 if (err)
385 415 return err;
386 416 }
387 417 if (m->m_module != NULL) {
388 418 err = visit(m->m_module, arg);
389 419 if (err)
390 420 return err;
391 421 }
392 422 return 0;
393 423 }
394 424
395 425 static PyObject *
396 426 meth_get__self__(PythonQtSlotFunctionObject *m, void * /*closure*/)
397 427 {
398 428 PyObject *self;
399 429 if (PyEval_GetRestricted()) {
400 430 PyErr_SetString(PyExc_RuntimeError,
401 431 "method.__self__ not accessible in restricted mode");
402 432 return NULL;
403 433 }
404 434 self = m->m_self;
405 435 if (self == NULL)
406 436 self = Py_None;
407 437 Py_INCREF(self);
408 438 return self;
409 439 }
410 440
411 441 static PyGetSetDef meth_getsets [] = {
412 442 {"__doc__", (getter)meth_get__doc__, NULL, NULL},
413 443 {"__name__", (getter)meth_get__name__, NULL, NULL},
414 444 {"__self__", (getter)meth_get__self__, NULL, NULL},
415 445 {NULL, NULL, NULL,NULL},
416 446 };
417 447
418 448 #if PY_MAJOR_VERSION == 2 && PY_MINOR_VERSION < 6
419 449 #define PY_WRITE_RESTRICTED WRITE_RESTRICTED
420 450 #endif
421 451
422 452 #define OFF(x) offsetof(PythonQtSlotFunctionObject, x)
423 453
424 454 static PyMemberDef meth_members[] = {
425 455 {"__module__", T_OBJECT, OFF(m_module), PY_WRITE_RESTRICTED},
426 456 {NULL}
427 457 };
428 458
429 459 static PyObject *
430 460 meth_repr(PythonQtSlotFunctionObject *f)
431 461 {
432 462 if (f->m_self->ob_type == &PythonQtClassWrapper_Type) {
433 463 PythonQtClassWrapper* self = (PythonQtClassWrapper*) f->m_self;
434 464 return PyString_FromFormat("<unbound qt slot %s of %s type>",
435 465 f->m_ml->slotName().data(),
436 466 self->classInfo()->className());
437 467 } else {
438 468 return PyString_FromFormat("<qt slot %s of %s instance at %p>",
439 469 f->m_ml->slotName().data(),
440 470 f->m_self->ob_type->tp_name,
441 471 f->m_self);
442 472 }
443 473 }
444 474
445 475 static int
446 476 meth_compare(PythonQtSlotFunctionObject *a, PythonQtSlotFunctionObject *b)
447 477 {
448 478 if (a->m_self != b->m_self)
449 479 return (a->m_self < b->m_self) ? -1 : 1;
450 480 if (a->m_ml == b->m_ml)
451 481 return 0;
452 482 if (strcmp(a->m_ml->metaMethod()->signature(), b->m_ml->metaMethod()->signature()) < 0)
453 483 return -1;
454 484 else
455 485 return 1;
456 486 }
457 487
458 488 static long
459 489 meth_hash(PythonQtSlotFunctionObject *a)
460 490 {
461 491 long x,y;
462 492 if (a->m_self == NULL)
463 493 x = 0;
464 494 else {
465 495 x = PyObject_Hash(a->m_self);
466 496 if (x == -1)
467 497 return -1;
468 498 }
469 499 y = _Py_HashPointer((void*)(a->m_ml));
470 500 if (y == -1)
471 501 return -1;
472 502 x ^= y;
473 503 if (x == -1)
474 504 x = -2;
475 505 return x;
476 506 }
477 507
478 508
479 509 PyTypeObject PythonQtSlotFunction_Type = {
480 510 PyObject_HEAD_INIT(&PyType_Type)
481 511 0,
482 512 "builtin_qt_slot",
483 513 sizeof(PythonQtSlotFunctionObject),
484 514 0,
485 515 (destructor)meth_dealloc, /* tp_dealloc */
486 516 0, /* tp_print */
487 517 0, /* tp_getattr */
488 518 0, /* tp_setattr */
489 519 (cmpfunc)meth_compare, /* tp_compare */
490 520 (reprfunc)meth_repr, /* tp_repr */
491 521 0, /* tp_as_number */
492 522 0, /* tp_as_sequence */
493 523 0, /* tp_as_mapping */
494 524 (hashfunc)meth_hash, /* tp_hash */
495 525 PythonQtSlotFunction_Call, /* tp_call */
496 526 0, /* tp_str */
497 527 PyObject_GenericGetAttr, /* tp_getattro */
498 528 0, /* tp_setattro */
499 529 0, /* tp_as_buffer */
500 530 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
501 531 0, /* tp_doc */
502 532 (traverseproc)meth_traverse, /* tp_traverse */
503 533 0, /* tp_clear */
504 534 0, /* tp_richcompare */
505 535 0, /* tp_weaklistoffset */
506 536 0, /* tp_iter */
507 537 0, /* tp_iternext */
508 538 0, /* tp_methods */
509 539 meth_members, /* tp_members */
510 540 meth_getsets, /* tp_getset */
511 541 0, /* tp_base */
512 542 0, /* tp_dict */
513 543 };
514 544
515 545 /* Clear out the free list */
516 546
517 547 void
518 548 PythonQtSlotFunction_Fini(void)
519 549 {
520 550 while (pythonqtslot_free_list) {
521 551 PythonQtSlotFunctionObject *v = pythonqtslot_free_list;
522 552 pythonqtslot_free_list = (PythonQtSlotFunctionObject *)(v->m_self);
523 553 PyObject_GC_Del(v);
524 554 }
525 555 }
526 556
General Comments 0
You need to be logged in to leave comments. Login now