* Added command line tool example similar to 'sopranocmd'
[kdebindings.git] / python / krosspython / pythonextension.cpp
bloba2f9ebada9660e884fd2b7c24b726f472a10db6f
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 )
166 delete d->object;
168 qDeleteAll(d->functions);//FIXME curently it may delete connections (i.e. PythonFunctions) that we want to stay!
169 delete d->proxymethod;
170 delete d;
173 QObject* PythonExtension::object() const
175 return d->object;
178 Py::Object PythonExtension::getattr(const char* n)
180 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
181 krossdebug( QString("PythonExtension::getattr name='%1'").arg(n) );
182 #endif
184 // handle internal methods
185 if(n[0] == '_') {
186 if(strcmp(n,"__methods__") == 0)
187 return d->methodnames;
188 if(strcmp(n,"__members__") == 0)
189 return d->membernames;
190 //if(strcmp(n,"__dict__") == 0)
191 // return PythonType<QStringList>::toPyObject( QStringList() );
192 if(strcmp(n,"__all__") == 0) //this hack is needed to prevent "from-import-* object has no __dict__ and no __all__" exceptions
193 return d->methodnames;
196 // look if the attribute is a method
197 if(d->methods.contains(n)) {
198 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
199 krossdebug( QString("PythonExtension::getattr name='%1' is a method.").arg(n) );
200 #endif
202 // Python is more dynamic then someone thinks of what provides us bigger problems
203 // here since we are not able to cache the whole lookup and need to create and return
204 // a new function object at each request since 1) they may used internaly by python
205 // to redirect this method/attr to somebody else and 2) those returned function object
206 // may prevent this object instance from beeing removed by the garbage collector.
208 //FIXME use callcache here to improve the performance by some factors!
209 Py::Tuple t(3);
210 t[0] = Py::Object(this); // reference to this instance, set at getattr()
211 t[1] = d->methods[n]; // the first index used for faster access
212 t[2] = Py::String(n); // the name of the method
213 t.increment_reference_count(); // the PyCFunction_New shoukd take care of removing our ref...
214 return Py::Object(PyCFunction_New( &d->proxymethod->ext_meth_def, t.ptr() ), true);
217 // look if the attribute is a property
218 if(d->properties.contains(n) && d->object) {
219 QMetaProperty property = d->properties[n];
221 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
222 krossdebug( QString("PythonExtension::getattr name='%1' is a property: type=%2 valid=%3 readable=%4 scriptable=%5 writable=%6 user=%7 usertype=%8")
223 .arg(n).arg(property.typeName()).arg(property.isValid())
224 .arg(property.isReadable()).arg(property.isScriptable(d->object)).arg(property.isWritable())
225 .arg(property.isUser(d->object)).arg(property.userType())
227 #endif
229 if(! property.isReadable()) {
230 Py::AttributeError( ::QString("Attribute \"%1\" is not readable.").arg(n).toLatin1().constData() );
231 return Py::None();
234 return PythonType<QVariant>::toPyObject( property.read(d->object) );
237 // look if the attribute is an enumerator
238 if(d->enumerations.contains(n)) {
239 return Py::Int(d->enumerations[n]);
242 // look if it's a dynamic property
243 if(d->object && d->object->dynamicPropertyNames().contains(n)) {
244 return PythonType<QVariant>::toPyObject( d->object->property(n) );
247 // finally redirect the unhandled attribute-request...
248 //return Py::PythonExtension<PythonExtension>::getattr_methods(n);
249 return Py::PythonExtension<PythonExtension>::getattr(n);
252 int PythonExtension::setattr(const char* n, const Py::Object& value)
254 // look if the attribute is a property
255 if(d->properties.contains(n) && d->object) {
256 QMetaProperty property = d->properties[n];
258 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
259 krossdebug( QString("PythonExtension::setattr name='%1' is a property: type=%2 valid=%3 readable=%4 scriptable=%5 writable=%6 usertype=%7")
260 .arg(n).arg(property.typeName()).arg(property.isValid())
261 .arg(property.isReadable()).arg(property.isScriptable(d->object)).arg(property.isWritable())
262 .arg(property.isUser(d->object)).arg(property.userType())
264 #endif
266 if(! property.isWritable()) {
267 Py::AttributeError( ::QString("Attribute \"%1\" is not writable.").arg(n).toLatin1().constData() );
268 return -1; // indicate error
271 QVariant v = PythonType<QVariant>::toVariant(value);
272 if(! property.write(d->object, v)) {
273 Py::AttributeError( ::QString("Setting attribute \"%1\" failed.").arg(n).toLatin1().constData() );
274 return -1; // indicate error
276 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
277 krossdebug( QString("PythonExtension::setattr name='%1' value='%2'").arg(n).arg(v.toString()) );
278 #endif
279 return 0; // indicate success
282 // finally redirect the unhandled attribute-request...
283 return Py::PythonExtension<PythonExtension>::setattr(n, value);
286 int PythonExtension::compare(const Py::Object& other)
288 if(Py::PythonExtension<PythonExtension>::check( other )) {
289 Py::ExtensionObject<PythonExtension> extobj( other );
290 PythonExtension* extension = extobj.extensionObject();
291 QObject* obj = extension->object();
292 //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()) );
293 //Q_ASSERT( obj );
294 //Q_ASSERT( object() );
295 return obj == object() ? 0 : ( obj > object() ? -1 : 1 );
297 PyErr_SetObject(PyExc_TypeError, other.ptr());
298 return -1;
301 long PythonExtension::hash()
303 return long( (QObject*) d->object );
306 /* objectName is a property anyway and therefore already accessible
307 Py::Object PythonExtension::getObjectName(const Py::Tuple&)
309 return PythonType<QString>::toPyObject( d->object->objectName() );
313 Py::Object PythonExtension::getClassName(const Py::Tuple&)
315 return PythonType<QString>::toPyObject( d->object->metaObject()->className() );
318 Py::Object PythonExtension::getSignalNames(const Py::Tuple&)
320 Py::List list;
321 const QMetaObject* metaobject = d->object->metaObject();
322 const int count = metaobject->methodCount();
323 for(int i = 0; i < count; ++i) {
324 QMetaMethod m = metaobject->method(i);
325 if( m.methodType() == QMetaMethod::Signal)
326 list.append( Py::String(m.signature()) );
328 return list;
331 Py::Object PythonExtension::getSlotNames(const Py::Tuple&)
333 Py::List list;
334 const QMetaObject* metaobject = d->object->metaObject();
335 const int count = metaobject->methodCount();
336 for(int i = 0; i < count; ++i) {
337 QMetaMethod m = metaobject->method(i);
338 if( m.methodType() == QMetaMethod::Slot)
339 list.append( Py::String(m.signature()) );
341 return list;
344 Py::Object PythonExtension::getPropertyNames(const Py::Tuple&)
346 Py::List list;
347 const QMetaObject* metaobject = d->object->metaObject();
348 const int count = metaobject->propertyCount();
349 for(int i = 0; i < count; ++i)
350 list.append( Py::String(metaobject->property(i).name()) );
351 return list;
354 Py::Object PythonExtension::getProperty(const Py::Tuple& args)
356 if( args.size() != 1 ) {
357 Py::TypeError("Expected the propertyname as argument.");
358 return Py::None();
360 return PythonType<QVariant>::toPyObject( d->object->property(
361 PythonType<QByteArray>::toVariant(args[0]).constData()
362 ) );
365 Py::Object PythonExtension::setProperty(const Py::Tuple& args)
367 if( args.size() != 2 ) {
368 Py::TypeError("Expected the propertyname and the value as arguments.");
369 return Py::None();
371 return PythonType<bool>::toPyObject( d->object->setProperty(
372 PythonType<QByteArray>::toVariant(args[0]).constData(),
373 PythonType<QVariant>::toVariant(args[1])
374 ) );
377 Py::Object PythonExtension::toPointer(const Py::Tuple&)
379 QObject* obj = d->object;
380 PyObject* qobjectptr = PyLong_FromVoidPtr( (void*) obj );
381 //PyObject* o = Py_BuildValue ("N", mw);
382 return Py::asObject( qobjectptr );
383 //PythonPyQtExtension* pyqtextension = new PythonPyQtExtension(self, args);
384 //return pyqtextension;
388 Py::Object PythonExtension::fromPointer(fromPointer(const Py::Tuple&)
390 QObject* object = dynamic_cast< QObject* >(PyLong_AsVoidPtr( args[0] ));
394 Py::Object PythonExtension::doConnect(const Py::Tuple& args)
396 #ifdef KROSS_PYTHON_EXTENSION_CONNECT_DEBUG
397 krossdebug( QString("PythonExtension::doConnect" ) );
398 for(uint i = 0; i < args.size(); ++i)
399 try {
400 QVariant v = PythonType<QVariant>::toVariant( args[i] );
401 krossdebug( QString(" Argument index=%1 variant.toString=%2 variant.typeName=%3").arg(i).arg(v.toString()).arg(v.typeName()) );
403 catch(Py::Exception& e) { // may happen e.g. on "function" types
404 PyTypeObject *type = (PyTypeObject*) args[i].type().ptr();
405 krossdebug( QString(" Argument index=%1 tp_name=%2").arg(i).arg(type->tp_name) );
406 e.clear();
408 #endif
410 if( args.size() < 2 ) {
411 Py::TypeError("Expected at least 2 arguments.");
412 return PythonType<bool>::toPyObject(false);
415 uint idx; // next argument to check
416 QObject* sender; // the sender object
417 QByteArray sendersignal; // the sender signal
418 if( args[0].isString() ) { // connect(signal, ...)
419 //krossdebug( QString("PythonExtension::doConnect connect(signal, ...)" ) );
420 sender = d->object;
421 sendersignal = PythonType<QByteArray>::toVariant( args[0] );
422 idx = 1;
424 else { // connect(sender, signal, ...)
425 //krossdebug( QString("PythonExtension::doConnect connect(sender, signal, ...)" ) );
426 Py::ExtensionObject<PythonExtension> extobj(args[0]);
427 PythonExtension* extension = extobj.extensionObject();
428 if(! extension) {
429 Py::TypeError( ::QString("First argument needs to be a signalname or a sender-object.").toLatin1().constData() );
430 return PythonType<bool>::toPyObject(false);
432 sender = extension->object();
433 if( ! args[1].isString() ) {
434 Py::TypeError( ::QString("Second argument needs to be a signalname.").toLatin1().constData() );
435 return PythonType<bool>::toPyObject(false);
437 sendersignal = PythonType<QByteArray>::toVariant( args[1] );
438 idx = 2;
439 if( args.size() <= idx ) {
440 Py::TypeError( ::QString("Expected at least %1 arguments.").arg(idx+1).toLatin1().constData() );
441 return PythonType<bool>::toPyObject(false);
445 QObject* receiver; // the receiver object
446 QByteArray receiverslot; // the receiver slot
447 if( args[idx].isCallable() ) { // connect with python function
448 //krossdebug( QString("PythonExtension::doConnect connect with python function" ) );
449 Py::Callable func(args[idx]); // the callable python function
450 PythonFunction* function = new PythonFunction(sender, sendersignal, func);
451 d->functions.insertMulti(sendersignal, function);
452 receiver = function;
453 receiverslot = sendersignal;
455 else { // connect with receiver and slot
456 //krossdebug( QString("PythonExtension::doConnect connect with receiver and slot" ) );
457 if( args[idx].isString() ) { // connect(..., slot)
458 receiver = d->object;
459 receiverslot = PythonType<QByteArray>::toVariant( args[idx] );
461 else { // connect(..., receiver, slot)
462 Py::ExtensionObject<PythonExtension> extobj(args[idx]);
463 PythonExtension* extension = extobj.extensionObject();
464 if(! extension) {
465 Py::TypeError( ::QString("Receiver argument needs to be a slotname or a receiver-object.").toLatin1().constData() );
466 return PythonType<bool>::toPyObject(false);
468 receiver = extension->object();
469 idx++;
470 if( args.size() < idx ) {
471 Py::TypeError( ::QString("Expected at least %1 arguments.").arg(idx+1).toLatin1().constData() );
472 return PythonType<bool>::toPyObject(false);
474 if( ! args[idx].isString() ) {
475 Py::TypeError( ::QString("Expected receiver slotname as argument %1.").arg(idx+1).toLatin1().constData() );
476 return PythonType<bool>::toPyObject(false);
478 receiverslot = PythonType<QByteArray>::toVariant( args[idx] );
482 // Dirty hack to replace SIGNAL() and SLOT() macros. If the user doesn't
483 // defined them explicit, we assume it's wanted to connect from a signal to
484 // a slot. This seems to be the most flexible solution so far...
485 if( ! sendersignal.startsWith('1') && ! sendersignal.startsWith('2') )
486 sendersignal.prepend('2'); // prepending 2 means SIGNAL(...)
487 if( ! receiverslot.startsWith('1') && ! receiverslot.startsWith('2') )
488 receiverslot.prepend('1'); // prepending 1 means SLOT(...)
490 #ifdef KROSS_PYTHON_EXTENSION_CONNECT_DEBUG
491 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() );
492 #endif
494 if(! QObject::connect(sender, sendersignal, receiver, receiverslot) ) {
495 krosswarning( QString("PythonExtension::doConnect Failed to connect").toLatin1().constData() );
496 return PythonType<bool>::toPyObject(false);
498 return PythonType<bool>::toPyObject(true);
501 Py::Object PythonExtension::doDisconnect(const Py::Tuple&)
503 //TODO
504 return PythonType<bool>::toPyObject(false);
507 PyObject* PythonExtension::proxyhandler(PyObject *_self_and_name_tuple, PyObject *args)
510 class GilState {
511 public:
512 GilState() { m_gilstate = PyGILState_Ensure(); } // Acquire interpreter lock
513 ~GilState() { PyGILState_Release( m_gilstate ); } // Free interpreter lock
514 private:
515 PyGILState_STATE m_gilstate;
517 GilState gil;
520 try {
521 Py::Tuple selftuple(_self_and_name_tuple);
523 int methodindex = Py::Int(selftuple[1]);
525 QByteArray ba = Py::String(selftuple[2]).as_string().c_str();
526 const char* methodname = ba.constData();
528 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
529 krossdebug( QString("PythonExtension::proxyhandler methodname=%1 methodindex=%2").arg(methodname).arg(methodindex) );
530 #endif
532 PythonExtension *self = static_cast<PythonExtension*>( selftuple[0].ptr() );
533 if( ! self->d->object ) {
534 Py::RuntimeError( QString("Underlying QObject instance of method '%1' was removed.").arg(methodname).toLatin1().constData() );
535 return Py_None;
538 Py::Tuple argstuple(args);
539 const int argssize = int( argstuple.size() );
540 QMetaMethod metamethod = self->d->object->metaObject()->method( methodindex );
541 if(metamethod.parameterTypes().size() != argssize) {
542 bool found = false;
543 const int count = self->d->object->metaObject()->methodCount();
544 for(++methodindex; methodindex < count; ++methodindex) {
545 metamethod = self->d->object->metaObject()->method( methodindex );
546 const QString signature = metamethod.signature();
547 const QByteArray name = signature.left(signature.indexOf('(')).toLatin1();
548 if(name == methodname) {
549 if(metamethod.parameterTypes().size() == argssize) {
550 found = true;
551 break;
555 if(! found) {
556 krosswarning( QString("PythonExtension::proxyhandler The method '%1' does not expect %2 arguments.").arg(methodname).arg(argssize) );
557 throw Py::TypeError( QString("Invalid number of arguments for the method %1").arg(methodname).toLatin1().constData() );
561 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
562 krossdebug( QString("PythonExtension::proxyhandler QMetaMethod idx=%1 sig=%2 tag=%3 type=%4").arg(methodindex).arg(metamethod.signature()).arg(metamethod.tag()).arg(metamethod.typeName()) );
563 for(int i = 0; i < argssize; ++i) {
564 QVariant v;
565 try {
566 v = PythonType<QVariant>::toVariant( argstuple[i] );
568 catch(Py::Exception& e) {
569 v = Py::value(e).as_string().c_str();
570 e.clear();
572 krossdebug( QString(" Argument index=%1 variant.toString=%2 variant.typeName=%3").arg(i).arg(v.toString()).arg(v.typeName()) );
574 #endif
576 Py::Object pyresult;
578 QList<QByteArray> typelist = metamethod.parameterTypes();
579 const int typelistcount = typelist.count();
580 bool hasreturnvalue = strcmp(metamethod.typeName(),"") != 0;
582 // exact 1 returnvalue + 0..9 arguments
583 Q_ASSERT(typelistcount <= 10);
584 QVarLengthArray<MetaType*> variantargs( typelistcount + 1 );
585 QVarLengthArray<void*> voidstarargs( typelistcount + 1 );
587 // set the return value
588 if(hasreturnvalue) {
589 MetaType* returntype = PythonMetaTypeFactory::create( metamethod.typeName(), Py::Object(), false /*owner*/ );
590 variantargs[0] = returntype;
591 voidstarargs[0] = returntype->toVoidStar();
592 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
593 krossdebug( QString("PythonExtension::proxyhandler typeName=%1").arg(metamethod.typeName()) );
594 #endif
596 else {
597 variantargs[0] = 0;
598 voidstarargs[0] = (void*)0;
601 // set the arguments
602 int idx = 1;
603 try {
604 for(; idx <= typelistcount; ++idx) {
605 variantargs[idx] = PythonMetaTypeFactory::create( typelist[idx-1].constData(), argstuple[idx - 1], false /*owner*/ );
606 voidstarargs[idx] = variantargs[idx]->toVoidStar();
609 catch(Py::Exception& e) {
610 // Seems PythonMetaTypeFactory::create raised an exception
611 // up. Clean all already allocated MetaType instances.
612 for(int i = 0; i < idx; ++i)
613 delete variantargs[i];
614 throw e; // re-throw exception
617 // call the method now
618 int r = self->d->object->qt_metacall(QMetaObject::InvokeMetaMethod, methodindex, &voidstarargs[0]);
619 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
620 krossdebug( QString("RESULT nr=%1").arg(r) );
621 #else
622 Q_UNUSED(r);
623 #endif
625 // eval the return-value
626 if(hasreturnvalue) {
627 QVariant v;
628 if( Kross::MetaTypeHandler* handler = Kross::Manager::self().metaTypeHandler(metamethod.typeName()) ) {
629 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
630 krossdebug( QString("Returnvalue of type '%2' has a handler").arg(metamethod.typeName()) );
631 #endif
632 void *ptr = (*reinterpret_cast<void*(*)>( variantargs[0]->toVoidStar() ));
633 v = handler->callHandler(ptr);
635 else {
636 v = QVariant(variantargs[0]->typeId(), variantargs[0]->toVoidStar());
638 if( ! Kross::Manager::self().strictTypesEnabled() ) {
639 if( v.type() == QVariant::Invalid && QByteArray(metamethod.typeName()).endsWith("*") ) {
640 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
641 krossdebug( QString("Returnvalue of type '%2' will be reinterpret_cast<QObject*>").arg(metamethod.typeName()) );
642 #endif
643 QObject* obj = (*reinterpret_cast<QObject*(*)>( variantargs[0]->toVoidStar() ));
644 v.setValue( (QObject*) obj );
648 pyresult = PythonType<QVariant>::toPyObject(v);
649 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
650 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()) );
651 #endif
654 // finally free the PythonVariable instances
655 for(int i = 0; i <= typelistcount; ++i)
656 delete variantargs[i];
659 pyresult.increment_reference_count(); // don't destroy PyObject* if pyresult got destroyed.
660 return pyresult.ptr();
662 catch(Py::Exception& e) {
663 QStringList trace;
664 int lineno;
665 PythonInterpreter::extractException(trace, lineno);
666 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")) );
667 PyErr_Print(); //e.clear();
670 return Py_None;
673 int PythonExtension::sequence_length()
675 return d->object->children().count();
678 Py::Object PythonExtension::sequence_concat(const Py::Object& obj)
680 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_concat %1").arg(obj.as_string().c_str()).toLatin1().constData() );
683 Py::Object PythonExtension::sequence_repeat(Py_ssize_t index)
685 // what we do here is rather evil and does not make sense at all. We assume that something
686 // like "myobjinstance * 2" means, that the pointer-address should be multiplied by 2. This
687 // is the case to keep backward-compatibility with SuperKaramba. In normal cases you wan't
688 // use such kind of logic within your python scripts anyway + we can't guarantee that the
689 // resulting number may not overflow or something like this.
690 return Py::Long( long( (QObject*) d->object ) * index );
693 Py::Object PythonExtension::sequence_item(Py_ssize_t index)
695 if(index < d->object->children().count())
696 return Py::asObject(new PythonExtension( d->object->children().at(index) ));
697 return Py::asObject( Py::new_reference_to( NULL ) );
700 Py::Object PythonExtension::sequence_slice(Py_ssize_t from, Py_ssize_t to)
702 Py::List list;
703 if(from >= 0) {
704 const int count = d->object->children().count();
705 for(int i = from; i <= to && i < count; ++i)
706 list.append( Py::asObject(new PythonExtension( d->object->children().at(i) )) );
708 return list;
711 int PythonExtension::sequence_ass_item(Py_ssize_t index, const Py::Object& obj)
713 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_ass_item %1 %2").arg(index).arg(obj.as_string().c_str()).toLatin1().constData() );
716 int PythonExtension::sequence_ass_slice(Py_ssize_t from, Py_ssize_t to, const Py::Object& obj)
718 throw Py::RuntimeError( QString("Unsupported: PythonExtension::sequence_ass_slice %1 %2 %3").arg(from).arg(to).arg(obj.as_string().c_str()).toLatin1().constData() );
721 int PythonExtension::mapping_length()
723 return d->object->children().count();
726 Py::Object PythonExtension::mapping_subscript(const Py::Object& obj)
728 QString name = Py::String(obj).as_string().c_str();
729 QObject* object = d->object->findChild< QObject* >( name );
730 if(! object) {
731 foreach(QObject* o, d->object->children()) {
732 if(name == o->metaObject()->className()) {
733 object = o;
734 break;
738 if(object)
739 return Py::asObject(new PythonExtension(object));
740 return Py::asObject( Py::new_reference_to( NULL ) );
743 int PythonExtension::mapping_ass_subscript(const Py::Object& obj1, const Py::Object& obj2)
745 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() );
748 int PythonExtension::number_nonzero()
750 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
751 krossdebug("PythonExtension::number_nonzero");
752 #endif
753 return d->object ? 1 : 0;
756 Py::Object PythonExtension::number_int()
758 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
759 krossdebug("PythonExtension::number_int");
760 #endif
761 return Py::Int( hash() );
764 Py::Object PythonExtension::number_long()
766 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
767 krossdebug("PythonExtension::number_long");
768 #endif
769 return Py::Long( hash() );
772 Py::Object PythonExtension::number_hex()
774 #ifdef KROSS_PYTHON_EXTENSION_NUMERIC_DEBUG
775 krossdebug("PythonExtension::number_hex");
776 #endif
777 void* ptr = (QObject*) d->object;
778 return Py::Object(PyString_FromFormat("%p",ptr),true);