* Don't build the kdevplatform smoke lib by default
[kdebindings.git] / python / krosspython / pythonextension.cpp
blobc9109ac2033c6ff92f21d6848b39a7080fb34c9c
1 /***************************************************************************
2 * pythonextension.cpp
3 * This file is part of the KDE project
4 * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
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 GNU
13 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public License
15 * along with this program; see the file COPYING. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 ***************************************************************************/
20 #include "pythonextension.h"
21 #include "pythoninterpreter.h"
22 #include "pythonvariant.h"
23 #include "pythonfunction.h"
25 #include <QWidget>
26 #include <QMetaMethod>
27 #include <QSignalSpy>
28 #include <QVarLengthArray>
30 namespace Kross {
32 /// \internal d-pointer class.
33 class PythonExtension::Private
35 public:
36 /// The QObject this PythonExtension wraps.
37 QPointer<QObject> object;
38 /// Defines if this PythonExtension the owner of the QObject.
39 bool owner;
41 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
42 /// \internal string for debugging.
43 QString debuginfo;
44 #endif
46 /// The cached list of methods.
47 QHash<QByteArray, Py::Int> methods;
48 /// The cached list of properties.
49 QHash<QByteArray, QMetaProperty> properties;
50 /// The cached list of enumerations.
51 QHash<QByteArray, int> enumerations;
53 /// The \a PythonFunction instances.
54 QHash<QByteArray, PythonFunction*> functions;
56 /// The cached list of methodnames.
57 Py::List methodnames;
58 /// The cached list of membernames.
59 Py::List membernames;
61 /// The proxymethod which will handle all calls to our \a PythonExtension instance.
62 Py::MethodDefExt<PythonExtension>* proxymethod;
68 using namespace Kross;
70 PythonExtension::PythonExtension(QObject* object, bool owner)
71 : Py::PythonExtension<PythonExtension>()
72 , d( new Private() )
74 d->object = object;
75 d->owner = owner;
77 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
78 d->debuginfo = object ? QString("%1 (%2)").arg(object->objectName()).arg(object->metaObject()->className()) : "NULL";
79 krossdebug( QString("PythonExtension::Constructor object=%1").arg(d->debuginfo) );
80 #endif
82 behaviors().name("KrossPythonExtension");
83 behaviors().doc("The KrossPythonExtension object wraps a QObject into the world of python.");
84 //behaviors().supportPrint();
85 behaviors().supportGetattr();
86 behaviors().supportSetattr();
87 //behaviors().supportGetattro();
88 //behaviors().supportSetattro();
89 behaviors().supportCompare();
90 //behaviors().supportRepr();
91 //behaviors().supportStr();
92 behaviors().supportHash();
93 //behaviors().supportIter();
94 //behaviors().supportCall();
95 behaviors().supportSequenceType();
96 behaviors().supportMappingType();
97 behaviors().supportNumberType();
98 //behaviors().supportBufferType();
100 add_varargs_method("className", &PythonExtension::getClassName, "Return the name of the QObject class.");
101 //add_varargs_method("classInfo", &PythonExtension::getClassInfo, "Return a list of key,value-tuples of class information.");
102 add_varargs_method("signalNames", &PythonExtension::getSignalNames, "Return list of signal names the QObject provides.");
103 add_varargs_method("slotNames", &PythonExtension::getSlotNames, "Return list of slot names the QObject provides.");
104 add_varargs_method("propertyNames", &PythonExtension::getPropertyNames, "Return list of property names the QObject provides.");
105 //add_varargs_method("dynamicPropertyNames", &PythonExtension::getDynamicPropertyNames, "");
106 add_varargs_method("property", &PythonExtension::getProperty, "Return a property value.");
107 add_varargs_method("setProperty", &PythonExtension::setProperty, "Set a property value.");
108 add_varargs_method("__toPointer__", &PythonExtension::toPointer, "Return the void* pointer of the QObject.");
109 //add_varargs_method("__fromPointer__", &PythonExtension::fromPointer, "Set the QObject* to the passed void* pointer.");
110 add_varargs_method("connect", &PythonExtension::doConnect, "Connect signal, slots or python functions together.");
111 add_varargs_method("disconnect", &PythonExtension::doDisconnect, "Disconnect signal, slots or python functions that are connected together.");
113 d->proxymethod = new Py::MethodDefExt<PythonExtension>(
114 "", // methodname, not needed cause we use the method only internaly.
115 0, // method that should handle the callback, not needed cause proxyhandler will handle it.
116 Py::method_varargs_call_handler_t( proxyhandler ), // callback handler
117 "" // documentation
120 if(d->object) {
121 const QMetaObject* metaobject = d->object->metaObject();
123 { // initialize methods.
124 const int count = metaobject->methodCount();
125 for(int i = 0; i < count; ++i) {
126 QMetaMethod member = metaobject->method(i);
127 const QString signature = member.signature();
128 const QByteArray name = signature.left(signature.indexOf('(')).toLatin1();
129 if(! d->methods.contains(name)) {
130 d->methods.insert(name, Py::Int(i));
131 d->methodnames.append(Py::String(name));
136 { // initialize properties
137 const int count = metaobject->propertyCount();
138 for(int i = 0; i < count; ++i) {
139 QMetaProperty prop = metaobject->property(i);
140 d->properties.insert(prop.name(), prop);
141 d->membernames.append( Py::String(prop.name()) );
145 { // initialize enumerations
146 const int count = metaobject->enumeratorCount();
147 for(int i = 0; i < count; ++i) {
148 QMetaEnum e = metaobject->enumerator(i);
149 const int kc = e.keyCount();
150 for(int k = 0; k < kc; ++k) {
151 const QByteArray name = /*e.name() +*/ e.key(k);
152 d->enumerations.insert(name, e.value(k));
153 d->membernames.append( Py::String(name) );
160 PythonExtension::~PythonExtension()
162 #ifdef KROSS_PYTHON_EXTENSION_CTORDTOR_DEBUG
163 krossdebug( QString("PythonExtension::Destructor object=%1").arg(d->debuginfo) );
164 #endif
165 if( d->owner )
167 delete d->object;
168 qDeleteAll(d->functions);
170 delete d->proxymethod;
171 delete d;
174 QObject* PythonExtension::object() const
176 return d->object;
179 Py::Object PythonExtension::getattr(const char* n)
181 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
182 krossdebug( QString("PythonExtension::getattr name='%1'").arg(n) );
183 #endif
185 // handle internal methods
186 if(n[0] == '_') {
187 if(strcmp(n,"__methods__") == 0)
188 return d->methodnames;
189 if(strcmp(n,"__members__") == 0)
190 return d->membernames;
191 //if(strcmp(n,"__dict__") == 0)
192 // return PythonType<QStringList>::toPyObject( QStringList() );
193 if(strcmp(n,"__all__") == 0) //this hack is needed to prevent "from-import-* object has no __dict__ and no __all__" exceptions
194 return d->methodnames;
197 // look if the attribute is a method
198 if(d->methods.contains(n)) {
199 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
200 krossdebug( QString("PythonExtension::getattr name='%1' is a method.").arg(n) );
201 #endif
203 // Python is more dynamic then someone thinks of what provides us bigger problems
204 // here since we are not able to cache the whole lookup and need to create and return
205 // a new function object at each request since 1) they may used internaly by python
206 // to redirect this method/attr to somebody else and 2) those returned function object
207 // may prevent this object instance from beeing removed by the garbage collector.
209 //FIXME use callcache here to improve the performance by some factors!
210 Py::Tuple t(3);
211 t[0] = Py::Object(this); // reference to this instance, set at getattr()
212 t[1] = d->methods[n]; // the first index used for faster access
213 t[2] = Py::String(n); // the name of the method
214 t.increment_reference_count(); // the PyCFunction_New shoukd take care of removing our ref...
215 return Py::Object(PyCFunction_New( &d->proxymethod->ext_meth_def, t.ptr() ), true);
218 // look if the attribute is a property
219 if(d->properties.contains(n) && d->object) {
220 QMetaProperty property = d->properties[n];
222 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
223 krossdebug( QString("PythonExtension::getattr name='%1' is a property: type=%2 valid=%3 readable=%4 scriptable=%5 writable=%6 user=%7 usertype=%8")
224 .arg(n).arg(property.typeName()).arg(property.isValid())
225 .arg(property.isReadable()).arg(property.isScriptable(d->object)).arg(property.isWritable())
226 .arg(property.isUser(d->object)).arg(property.userType())
228 #endif
230 if(! property.isReadable()) {
231 Py::AttributeError( ::QString("Attribute \"%1\" is not readable.").arg(n).toLatin1().constData() );
232 return Py::None();
235 return PythonType<QVariant>::toPyObject( property.read(d->object) );
238 // look if the attribute is an enumerator
239 if(d->enumerations.contains(n)) {
240 return Py::Int(d->enumerations[n]);
243 // finally redirect the unhandled attribute-request...
244 //return Py::PythonExtension<PythonExtension>::getattr_methods(n);
245 return Py::PythonExtension<PythonExtension>::getattr(n);
248 int PythonExtension::setattr(const char* n, const Py::Object& value)
250 // look if the attribute is a property
251 if(d->properties.contains(n) && d->object) {
252 QMetaProperty property = d->properties[n];
254 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
255 krossdebug( QString("PythonExtension::setattr name='%1' is a property: type=%2 valid=%3 readable=%4 scriptable=%5 writable=%6 usertype=%7")
256 .arg(n).arg(property.typeName()).arg(property.isValid())
257 .arg(property.isReadable()).arg(property.isScriptable(d->object)).arg(property.isWritable())
258 .arg(property.isUser(d->object)).arg(property.userType())
260 #endif
262 if(! property.isWritable()) {
263 Py::AttributeError( ::QString("Attribute \"%1\" is not writable.").arg(n).toLatin1().constData() );
264 return -1; // indicate error
267 QVariant v = PythonType<QVariant>::toVariant(value);
268 if(! property.write(d->object, v)) {
269 Py::AttributeError( ::QString("Setting attribute \"%1\" failed.").arg(n).toLatin1().constData() );
270 return -1; // indicate error
272 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
273 krossdebug( QString("PythonExtension::setattr name='%1' value='%2'").arg(n).arg(v.toString()) );
274 #endif
275 return 0; // indicate success
278 // finally redirect the unhandled attribute-request...
279 return Py::PythonExtension<PythonExtension>::setattr(n, value);
282 int PythonExtension::compare(const Py::Object& other)
284 if(Py::PythonExtension<PythonExtension>::check( other )) {
285 Py::ExtensionObject<PythonExtension> extobj( other );
286 PythonExtension* extension = extobj.extensionObject();
287 QObject* obj = extension->object();
288 //krossdebug( QString("PythonExtension::compare this.name='%1' other.name='%2' other.pyobject='%3'").arg(d->object ? d->object->objectName() : "NULL").arg(obj ? obj->objectName() : "NULL").arg(other.as_string().c_str()) );
289 //Q_ASSERT( obj );
290 //Q_ASSERT( object() );
291 return obj == object() ? 0 : ( obj > object() ? -1 : 1 );
293 PyErr_SetObject(PyExc_TypeError, other.ptr());
294 return -1;
297 long PythonExtension::hash()
299 return long( (QObject*) d->object );
302 /* objectName is a property anyway and therefore already accessible
303 Py::Object PythonExtension::getObjectName(const Py::Tuple&)
305 return PythonType<QString>::toPyObject( d->object->objectName() );
309 Py::Object PythonExtension::getClassName(const Py::Tuple&)
311 return PythonType<QString>::toPyObject( d->object->metaObject()->className() );
314 Py::Object PythonExtension::getSignalNames(const Py::Tuple&)
316 Py::List list;
317 const QMetaObject* metaobject = d->object->metaObject();
318 const int count = metaobject->methodCount();
319 for(int i = 0; i < count; ++i) {
320 QMetaMethod m = metaobject->method(i);
321 if( m.methodType() == QMetaMethod::Signal)
322 list.append( Py::String(m.signature()) );
324 return list;
327 Py::Object PythonExtension::getSlotNames(const Py::Tuple&)
329 Py::List list;
330 const QMetaObject* metaobject = d->object->metaObject();
331 const int count = metaobject->methodCount();
332 for(int i = 0; i < count; ++i) {
333 QMetaMethod m = metaobject->method(i);
334 if( m.methodType() == QMetaMethod::Slot)
335 list.append( Py::String(m.signature()) );
337 return list;
340 Py::Object PythonExtension::getPropertyNames(const Py::Tuple&)
342 Py::List list;
343 const QMetaObject* metaobject = d->object->metaObject();
344 const int count = metaobject->propertyCount();
345 for(int i = 0; i < count; ++i)
346 list.append( Py::String(metaobject->property(i).name()) );
347 return list;
350 Py::Object PythonExtension::getProperty(const Py::Tuple& args)
352 if( args.size() != 1 ) {
353 Py::TypeError("Expected the propertyname as argument.");
354 return Py::None();
356 return PythonType<QVariant>::toPyObject( d->object->property(
357 PythonType<QByteArray>::toVariant(args[0]).constData()
358 ) );
361 Py::Object PythonExtension::setProperty(const Py::Tuple& args)
363 if( args.size() != 2 ) {
364 Py::TypeError("Expected the propertyname and the value as arguments.");
365 return Py::None();
367 return PythonType<bool>::toPyObject( d->object->setProperty(
368 PythonType<QByteArray>::toVariant(args[0]).constData(),
369 PythonType<QVariant>::toVariant(args[1])
370 ) );
373 Py::Object PythonExtension::toPointer(const Py::Tuple&)
375 QObject* obj = d->object;
376 PyObject* qobjectptr = PyLong_FromVoidPtr( (void*) obj );
377 //PyObject* o = Py_BuildValue ("N", mw);
378 return Py::asObject( qobjectptr );
379 //PythonPyQtExtension* pyqtextension = new PythonPyQtExtension(self, args);
380 //return pyqtextension;
384 Py::Object PythonExtension::fromPointer(fromPointer(const Py::Tuple&)
386 QObject* object = dynamic_cast< QObject* >(PyLong_AsVoidPtr( args[0] ));
390 Py::Object PythonExtension::doConnect(const Py::Tuple& args)
392 #ifdef KROSS_PYTHON_EXTENSION_CONNECT_DEBUG
393 krossdebug( QString("PythonExtension::doConnect" ) );
394 for(uint i = 0; i < args.size(); ++i)
395 try {
396 QVariant v = PythonType<QVariant>::toVariant( args[i] );
397 krossdebug( QString(" Argument index=%1 variant.toString=%2 variant.typeName=%3").arg(i).arg(v.toString()).arg(v.typeName()) );
399 catch(Py::Exception& e) { // may happen e.g. on "function" types
400 PyTypeObject *type = (PyTypeObject*) args[i].type().ptr();
401 krossdebug( QString(" Argument index=%1 tp_name=%2").arg(i).arg(type->tp_name) );
402 e.clear();
404 #endif
406 if( args.size() < 2 ) {
407 Py::TypeError("Expected at least 2 arguments.");
408 return PythonType<bool>::toPyObject(false);
411 uint idx; // next argument to check
412 QObject* sender; // the sender object
413 QByteArray sendersignal; // the sender signal
414 if( args[0].isString() ) { // connect(signal, ...)
415 //krossdebug( QString("PythonExtension::doConnect connect(signal, ...)" ) );
416 sender = d->object;
417 sendersignal = PythonType<QByteArray>::toVariant( args[0] );
418 idx = 1;
420 else { // connect(sender, signal, ...)
421 //krossdebug( QString("PythonExtension::doConnect connect(sender, signal, ...)" ) );
422 Py::ExtensionObject<PythonExtension> extobj(args[0]);
423 PythonExtension* extension = extobj.extensionObject();
424 if(! extension) {
425 Py::TypeError( ::QString("First argument needs to be a signalname or a sender-object.").toLatin1().constData() );
426 return PythonType<bool>::toPyObject(false);
428 sender = extension->object();
429 if( ! args[1].isString() ) {
430 Py::TypeError( ::QString("Second argument needs to be a signalname.").toLatin1().constData() );
431 return PythonType<bool>::toPyObject(false);
433 sendersignal = PythonType<QByteArray>::toVariant( args[1] );
434 idx = 2;
435 if( args.size() <= idx ) {
436 Py::TypeError( ::QString("Expected at least %1 arguments.").arg(idx+1).toLatin1().constData() );
437 return PythonType<bool>::toPyObject(false);
441 QObject* receiver; // the receiver object
442 QByteArray receiverslot; // the receiver slot
443 if( args[idx].isCallable() ) { // connect with python function
444 //krossdebug( QString("PythonExtension::doConnect connect with python function" ) );
445 Py::Callable func(args[idx]); // the callable python function
446 PythonFunction* function = new PythonFunction(sender, sendersignal, func);
447 d->functions.insertMulti(sendersignal, function);
448 receiver = function;
449 receiverslot = sendersignal;
451 else { // connect with receiver and slot
452 //krossdebug( QString("PythonExtension::doConnect connect with receiver and slot" ) );
453 if( args[idx].isString() ) { // connect(..., slot)
454 receiver = d->object;
455 receiverslot = PythonType<QByteArray>::toVariant( args[idx] );
457 else { // connect(..., receiver, slot)
458 Py::ExtensionObject<PythonExtension> extobj(args[idx]);
459 PythonExtension* extension = extobj.extensionObject();
460 if(! extension) {
461 Py::TypeError( ::QString("Receiver argument needs to be a slotname or a receiver-object.").toLatin1().constData() );
462 return PythonType<bool>::toPyObject(false);
464 receiver = extension->object();
465 idx++;
466 if( args.size() < idx ) {
467 Py::TypeError( ::QString("Expected at least %1 arguments.").arg(idx+1).toLatin1().constData() );
468 return PythonType<bool>::toPyObject(false);
470 if( ! args[idx].isString() ) {
471 Py::TypeError( ::QString("Expected receiver slotname as argument %1.").arg(idx+1).toLatin1().constData() );
472 return PythonType<bool>::toPyObject(false);
474 receiverslot = PythonType<QByteArray>::toVariant( args[idx] );
478 // Dirty hack to replace SIGNAL() and SLOT() macros. If the user doesn't
479 // defined them explicit, we assume it's wanted to connect from a signal to
480 // a slot. This seems to be the most flexible solution so far...
481 if( ! sendersignal.startsWith('1') && ! sendersignal.startsWith('2') )
482 sendersignal.prepend('2'); // prepending 2 means SIGNAL(...)
483 if( ! receiverslot.startsWith('1') && ! receiverslot.startsWith('2') )
484 receiverslot.prepend('1'); // prepending 1 means SLOT(...)
486 #ifdef KROSS_PYTHON_EXTENSION_CONNECT_DEBUG
487 krossdebug( QString("PythonExtension::doConnect sender=%1 signal=%2 receiver=%3 slot=%4").arg(sender->objectName()).arg(sendersignal.constData()).arg(receiver->objectName()).arg(receiverslot.constData()).toLatin1().constData() );
488 #endif
490 if(! QObject::connect(sender, sendersignal, receiver, receiverslot) ) {
491 krosswarning( QString("PythonExtension::doConnect Failed to connect").toLatin1().constData() );
492 return PythonType<bool>::toPyObject(false);
494 return PythonType<bool>::toPyObject(true);
497 Py::Object PythonExtension::doDisconnect(const Py::Tuple&)
499 //TODO
500 return PythonType<bool>::toPyObject(false);
503 PyObject* PythonExtension::proxyhandler(PyObject *_self_and_name_tuple, PyObject *args)
506 class GilState {
507 public:
508 GilState() { m_gilstate = PyGILState_Ensure(); } // Acquire interpreter lock
509 ~GilState() { PyGILState_Release( m_gilstate ); } // Free interpreter lock
510 private:
511 PyGILState_STATE m_gilstate;
513 GilState gil;
516 try {
517 Py::Tuple selftuple(_self_and_name_tuple);
519 int methodindex = Py::Int(selftuple[1]);
521 QByteArray ba = Py::String(selftuple[2]).as_string().c_str();
522 const char* methodname = ba.constData();
524 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
525 krossdebug( QString("PythonExtension::proxyhandler methodname=%1 methodindex=%2").arg(methodname).arg(methodindex) );
526 #endif
528 PythonExtension *self = static_cast<PythonExtension*>( selftuple[0].ptr() );
529 if( ! self->d->object ) {
530 Py::RuntimeError( QString("Underlying QObject instance of method '%1' was removed.").arg(methodname).toLatin1().constData() );
531 return Py_None;
534 Py::Tuple argstuple(args);
535 const int argssize = int( argstuple.size() );
536 QMetaMethod metamethod = self->d->object->metaObject()->method( methodindex );
537 if(metamethod.parameterTypes().size() != argssize) {
538 bool found = false;
539 const int count = self->d->object->metaObject()->methodCount();
540 for(++methodindex; methodindex < count; ++methodindex) {
541 metamethod = self->d->object->metaObject()->method( methodindex );
542 const QString signature = metamethod.signature();
543 const QByteArray name = signature.left(signature.indexOf('(')).toLatin1();
544 if(name == methodname) {
545 if(metamethod.parameterTypes().size() == argssize) {
546 found = true;
547 break;
551 if(! found) {
552 krosswarning( QString("PythonExtension::proxyhandler The method '%1' does not expect %2 arguments.").arg(methodname).arg(argssize) );
553 throw Py::TypeError( QString("Invalid number of arguments for the method %1").arg(methodname).toLatin1().constData() );
557 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
558 krossdebug( QString("PythonExtension::proxyhandler QMetaMethod idx=%1 sig=%2 tag=%3 type=%4").arg(methodindex).arg(metamethod.signature()).arg(metamethod.tag()).arg(metamethod.typeName()) );
559 for(int i = 0; i < argssize; ++i) {
560 QVariant v;
561 try {
562 v = PythonType<QVariant>::toVariant( argstuple[i] );
564 catch(Py::Exception& e) {
565 v = Py::value(e).as_string().c_str();
566 e.clear();
568 krossdebug( QString(" Argument index=%1 variant.toString=%2 variant.typeName=%3").arg(i).arg(v.toString()).arg(v.typeName()) );
570 #endif
572 Py::Object pyresult;
574 QList<QByteArray> typelist = metamethod.parameterTypes();
575 const int typelistcount = typelist.count();
576 bool hasreturnvalue = strcmp(metamethod.typeName(),"") != 0;
578 // exact 1 returnvalue + 0..9 arguments
579 Q_ASSERT(typelistcount <= 10);
580 QVarLengthArray<MetaType*> variantargs( typelistcount + 1 );
581 QVarLengthArray<void*> voidstarargs( typelistcount + 1 );
583 // set the return value
584 if(hasreturnvalue) {
585 MetaType* returntype = PythonMetaTypeFactory::create( metamethod.typeName(), Py::Object(), false /*owner*/ );
586 variantargs[0] = returntype;
587 voidstarargs[0] = returntype->toVoidStar();
588 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
589 krossdebug( QString("PythonExtension::proxyhandler typeName=%1").arg(metamethod.typeName()) );
590 #endif
592 else {
593 variantargs[0] = 0;
594 voidstarargs[0] = (void*)0;
597 // set the arguments
598 int idx = 1;
599 try {
600 for(; idx <= typelistcount; ++idx) {
601 variantargs[idx] = PythonMetaTypeFactory::create( typelist[idx-1].constData(), argstuple[idx - 1], false /*owner*/ );
602 voidstarargs[idx] = variantargs[idx]->toVoidStar();
605 catch(Py::Exception& e) {
606 // Seems PythonMetaTypeFactory::create raised an exception
607 // up. Clean all already allocated MetaType instances.
608 for(int i = 0; i < idx; ++i)
609 delete variantargs[i];
610 throw e; // re-throw exception
613 // call the method now
614 int r = self->d->object->qt_metacall(QMetaObject::InvokeMetaMethod, methodindex, &voidstarargs[0]);
615 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
616 krossdebug( QString("RESULT nr=%1").arg(r) );
617 #else
618 Q_UNUSED(r);
619 #endif
621 // eval the return-value
622 if(hasreturnvalue) {
623 QVariant v;
624 if( Kross::MetaTypeHandler* handler = Kross::Manager::self().metaTypeHandler(metamethod.typeName()) ) {
625 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
626 krossdebug( QString("Returnvalue of type '%2' has a handler").arg(metamethod.typeName()) );
627 #endif
628 void *ptr = (*reinterpret_cast<void*(*)>( variantargs[0]->toVoidStar() ));
629 v = handler->callHandler(ptr);
631 else {
632 v = QVariant(variantargs[0]->typeId(), variantargs[0]->toVoidStar());
634 if( ! Kross::Manager::self().strictTypesEnabled() ) {
635 if( v.type() == QVariant::Invalid && QByteArray(metamethod.typeName()).endsWith("*") ) {
636 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
637 krossdebug( QString("Returnvalue of type '%2' will be reinterpret_cast<QObject*>").arg(d->metamethod.typeName()) );
638 #endif
639 QObject* obj = (*reinterpret_cast<QObject*(*)>( variantargs[0]->toVoidStar() ));
640 v.setValue( (QObject*) obj );
644 pyresult = PythonType<QVariant>::toPyObject(v);
645 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
646 krossdebug( QString("Returnvalue typeId=%1 metamethod.typename=%2 variant.toString=%3 variant.typeName=%4 pyobject=%5").arg(variantargs[0]->typeId()).arg(metamethod.typeName()).arg(v.toString()).arg(v.typeName()).arg(pyresult.as_string().c_str()) );
647 #endif
650 // finally free the PythonVariable instances
651 for(int i = 0; i <= typelistcount; ++i)
652 delete variantargs[i];
655 pyresult.increment_reference_count(); // don't destroy PyObject* if pyresult got destroyed.
656 return pyresult.ptr();
658 catch(Py::Exception& e) {
659 QStringList trace;
660 int lineno;
661 PythonInterpreter::extractException(trace, lineno);
662 krosswarning( QString("PythonExtension::proxyhandler Had exception on line %1:\n%2 \n%3").arg(lineno).arg(Py::value(e).as_string().c_str()).arg(trace.join("\n")) );
663 PyErr_Print(); //e.clear();
666 return Py_None;
669 int PythonExtension::sequence_length()
671 return d->object->children().count();
674 Py::Object PythonExtension::sequence_concat(const Py::Object& obj)
676 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_concat %1").arg(obj.as_string().c_str()).toLatin1().constData() );
679 Py::Object PythonExtension::sequence_repeat(Py_ssize_t index)
681 // what we do here is rather evil and does not make sense at all. We assume that something
682 // like "myobjinstance * 2" means, that the pointer-address should be multiplied by 2. This
683 // is the case to keep backward-compatibility with SuperKaramba. In normal cases you wan't
684 // use such kind of logic within your python scripts anyway + we can't guarantee that the
685 // resulting number may not overflow or something like this.
686 return Py::Long( long( (QObject*) d->object ) * index );
689 Py::Object PythonExtension::sequence_item(Py_ssize_t index)
691 if(index < d->object->children().count())
692 return Py::asObject(new PythonExtension( d->object->children().at(index) ));
693 return Py::asObject( Py::new_reference_to( NULL ) );
696 Py::Object PythonExtension::sequence_slice(Py_ssize_t from, Py_ssize_t to)
698 Py::List list;
699 if(from >= 0) {
700 const int count = d->object->children().count();
701 for(int i = from; i <= to && i < count; ++i)
702 list.append( Py::asObject(new PythonExtension( d->object->children().at(i) )) );
704 return list;
707 int PythonExtension::sequence_ass_item(Py_ssize_t index, const Py::Object& obj)
709 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_ass_item %1 %2").arg(index).arg(obj.as_string().c_str()).toLatin1().constData() );
712 int PythonExtension::sequence_ass_slice(Py_ssize_t from, Py_ssize_t to, const Py::Object& obj)
714 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_ass_slice %1 %2 %3").arg(from).arg(to).arg(obj.as_string().c_str()).toLatin1().constData() );
717 int PythonExtension::mapping_length()
719 return d->object->children().count();
722 Py::Object PythonExtension::mapping_subscript(const Py::Object& obj)
724 QString name = Py::String(obj).as_string().c_str();
725 QObject* object = d->object->findChild< QObject* >( name );
726 if(! object) {
727 foreach(QObject* o, d->object->children()) {
728 if(name == o->metaObject()->className()) {
729 object = o;
730 break;
734 if(object)
735 return Py::asObject(new PythonExtension(object));
736 return Py::asObject( Py::new_reference_to( NULL ) );
739 int PythonExtension::mapping_ass_subscript(const Py::Object& obj1, const Py::Object& obj2)
741 throw Py::RuntimeError( QString("Unsupported: PythonExtension::mapping_ass_subscript %1 %2").arg(obj1.as_string().c_str()).arg(obj2.as_string().c_str()).toLatin1().constData() );
744 int PythonExtension::number_nonzero()
746 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
747 krossdebug("PythonExtension::number_nonzero");
748 #endif
749 return d->object ? 1 : 0;
752 Py::Object PythonExtension::number_int()
754 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
755 krossdebug("PythonExtension::number_int");
756 #endif
757 return Py::Int( hash() );
760 Py::Object PythonExtension::number_long()
762 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
763 krossdebug("PythonExtension::number_long");
764 #endif
765 return Py::Long( hash() );
768 Py::Object PythonExtension::number_hex()
770 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
771 krossdebug("PythonExtension::number_hex");
772 #endif
773 void* ptr = (QObject*) d->object;
774 return Py::Object(PyString_FromFormat("%p",ptr),true);