lib

pythonextension.cpp

00001 /***************************************************************************
00002  * pythonextension.cpp
00003  * This file is part of the KDE project
00004  * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU Library General Public
00008  * License as published by the Free Software Foundation; either
00009  * version 2 of the License, or (at your option) any later version.
00010  * This program is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  * Library General Public License for more details.
00014  * You should have received a copy of the GNU Library General Public License
00015  * along with this program; see the file COPYING.  If not, write to
00016  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018  ***************************************************************************/
00019 
00020 #include "pythonextension.h"
00021 #include "pythonobject.h"
00022 
00023 #include "../api/variant.h"
00024 #include "../api/dict.h"
00025 #include "../api/exception.h"
00026 
00027 using namespace Kross::Python;
00028 
00029 PythonExtension::PythonExtension(Kross::Api::Object::Ptr object)
00030     : Py::PythonExtension<PythonExtension>()
00031     , m_object(object)
00032 {
00033 #ifdef KROSS_PYTHON_EXTENSION_CTOR_DEBUG
00034     krossdebug( QString("Kross::Python::PythonExtension::Constructor objectname='%1' objectclass='%2'").arg(m_object->getName()).arg(m_object->getClassName()) );
00035 #endif
00036 
00037     behaviors().name("KrossPythonExtension");
00038     /*
00039     behaviors().doc(
00040         "The common KrossPythonExtension object enables passing "
00041         "of Kross::Api::Object's from C/C++ to Python and "
00042         "backwards in a transparent way."
00043     );
00044     */
00045     behaviors().supportGetattr();
00046 
00047     m_proxymethod = new Py::MethodDefExt<PythonExtension>(
00048         "", // methodname, not needed cause we use the method only internaly.
00049         0, // method that should handle the callback, not needed cause proxyhandler will handle it.
00050         Py::method_varargs_call_handler_t( proxyhandler ), // callback handler
00051         "" // documentation
00052     );
00053 }
00054 
00055 PythonExtension::~PythonExtension()
00056 {
00057 #ifdef KROSS_PYTHON_EXTENSION_DTOR_DEBUG
00058     krossdebug( QString("Kross::Python::PythonExtension::Destructor objectname='%1' objectclass='%2'").arg(m_object->getName()).arg(m_object->getClassName()) );
00059 #endif
00060     delete m_proxymethod;
00061 }
00062 
00063 #if 0
00064 Py::Object PythonExtension::str()
00065 {
00066     Kross::Api::Callable* callable = dynamic_cast< Kross::Api::Callable* >(m_object);
00067     QString s = callable ? callable->getName() : m_object->getClassName();
00068     return toPyObject(s.isEmpty() ?  : s);
00069 }
00070 
00071 Py::Object PythonExtension::repr()
00072 {
00073     return toPyObject( m_object->toString() );
00074 }
00075 #endif
00076 
00077 Py::Object PythonExtension::getattr(const char* n)
00078 {
00079 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
00080     krossdebug( QString("Kross::Python::PythonExtension::getattr name='%1'").arg(n) );
00081 #endif
00082 
00083     if(n[0] == '_') {
00084         if(!strcmp(n,  "__methods__")) {
00085             Py::List methods;
00086             QStringList calls = m_object->getCalls();
00087             for(QStringList::Iterator it = calls.begin(); it != calls.end(); ++it) {
00088 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
00089                 krossdebug( QString("Kross::Python::PythonExtension::getattr name='%1' callable='%2'").arg(n).arg(*it) );
00090 #endif
00091                 methods.append(Py::String( (*it).latin1() ));
00092             }
00093             return methods;
00094         }
00095 
00096         if(!strcmp(n, "__members__")) {
00097             Py::List members;
00098             Kross::Api::Callable* callable = dynamic_cast<Kross::Api::Callable*>(m_object.data());
00099             if(callable) {
00100                 QMap<QString, Kross::Api::Object::Ptr> children = callable->getChildren();
00101                 QMap<QString, Kross::Api::Object::Ptr>::Iterator it( children.begin() );
00102                 for(; it != children.end(); ++it) {
00103 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
00104                     krossdebug( QString("Kross::Python::PythonExtension::getattr n='%1' child='%2'").arg(n).arg(it.key()) );
00105 #endif
00106                     members.append(Py::String( it.key().latin1() ));
00107                 }
00108             }
00109             return members;
00110         }
00111 
00112         //if(n == "__dict__") { krosswarning( QString("PythonExtension::getattr(%1) __dict__").arg(n) ); return Py::None(); }
00113         //if(n == "__class__") { krosswarning( QString("PythonExtension::getattr(%1) __class__").arg(n) ); return Py::None(); }
00114 
00115 #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
00116         krossdebug( QString("Kross::Python::PythonExtension::getattr name='%1' is a internal name.").arg(n) );
00117 #endif
00118         return Py::PythonExtension<PythonExtension>::getattr_methods(n);
00119     }
00120 
00121     // Redirect the call to our static proxy method which will take care
00122     // of handling the call.
00123     Py::Tuple self(2);
00124     self[0] = Py::Object(this);
00125     self[1] = Py::String(n);
00126     return Py::Object(PyCFunction_New( &m_proxymethod->ext_meth_def, self.ptr() ), true);
00127 }
00128 
00129 /*
00130 Py::Object PythonExtension::getattr_methods(const char* n)
00131 {
00132 #ifdef KROSS_PYTHON_EXTENSION_GETATTRMETHOD_DEBUG
00133     krossdebug( QString("PythonExtension::getattr_methods name=%1").arg(n) );
00134 #endif
00135     return Py::PythonExtension<PythonExtension>::getattr_methods(n);
00136 }
00137 
00138 int PythonExtension::setattr(const char* name, const Py::Object& value)
00139 {
00140 #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
00141     krossdebug( QString("PythonExtension::setattr name=%1 value=%2").arg(name).arg(value.as_string().c_str()) );
00142 #endif
00143     return Py::PythonExtension<PythonExtension>::setattr(name, value);
00144 }
00145 */
00146 
00147 Kross::Api::List::Ptr PythonExtension::toObject(const Py::Tuple& tuple)
00148 {
00149 #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
00150     krossdebug( QString("Kross::Python::PythonExtension::toObject(Py::Tuple)") );
00151 #endif
00152 
00153     QValueList<Kross::Api::Object::Ptr> l;
00154     uint size = tuple.size();
00155     for(uint i = 0; i < size; i++)
00156         l.append( toObject( tuple[i] ) );
00157     return new Kross::Api::List(l);
00158 }
00159 
00160 Kross::Api::List::Ptr PythonExtension::toObject(const Py::List& list)
00161 {
00162 #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
00163     krossdebug( QString("Kross::Python::PythonExtension::toObject(Py::List)") );
00164 #endif
00165 
00166     QValueList<Kross::Api::Object::Ptr> l;
00167     uint length = list.length();
00168     for(uint i = 0; i < length; i++)
00169         l.append( toObject( list[i] ) );
00170     return new Kross::Api::List(l);
00171 }
00172 
00173 Kross::Api::Dict::Ptr PythonExtension::toObject(const Py::Dict& dict)
00174 {
00175     QMap<QString, Kross::Api::Object::Ptr> map;
00176     Py::List l = dict.keys();
00177     uint length = l.length();
00178     for(Py::List::size_type i = 0; i < length; ++i) {
00179         const char* n = l[i].str().as_string().c_str();
00180         map.replace(n, toObject( dict[n] ));
00181     }
00182     return new Kross::Api::Dict(map);
00183 }
00184 
00185 Kross::Api::Object::Ptr PythonExtension::toObject(const Py::Object& object)
00186 {
00187 #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
00188     krossdebug( QString("Kross::Python::PythonExtension::toObject(Py::Object) object='%1'").arg(object.as_string().c_str()) );
00189 #endif
00190     if(object == Py::None())
00191         return 0;
00192     PyTypeObject *type = (PyTypeObject*) object.type().ptr();
00193 #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
00194     krossdebug( QString("Kross::Python::PythonExtension::toObject(Py::Object) type='%1'").arg(type->tp_name) );
00195 #endif
00196     if(type == &PyInt_Type)
00197         return new Kross::Api::Variant(int(Py::Int(object)));
00198     if(type == &PyBool_Type)
00199         return new Kross::Api::Variant(QVariant(object.isTrue(),0));
00200     if(type == &PyLong_Type)
00201         return new Kross::Api::Variant(Q_LLONG(long(Py::Long(object))));
00202     if(type == &PyFloat_Type)
00203         return new Kross::Api::Variant(double(Py::Float(object)));
00204 
00205     if( PyType_IsSubtype(type,&PyString_Type) ) {
00206 #ifdef Py_USING_UNICODE
00207         /* TODO
00208         if(type == &PyUnicode_Type) {
00209             Py::unicodestring u = Py::String(object).as_unicodestring();
00210             std::string s;
00211             std::copy(u.begin(), u.end(), std::back_inserter(s));
00212             return new Kross::Api::Variant(s.c_str());
00213         }
00214         */
00215 #endif
00216         return new Kross::Api::Variant(object.as_string().c_str());
00217     }
00218 
00219     if(type == &PyTuple_Type)
00220         return toObject(Py::Tuple(object)).data();
00221     if(type == &PyList_Type)
00222         return toObject(Py::List(object)).data();
00223     if(type == &PyDict_Type)
00224         return toObject(Py::Dict(object.ptr())).data();
00225 
00226     if(object.isInstance())
00227         return new PythonObject(object);
00228 
00229     Py::ExtensionObject<PythonExtension> extobj(object);
00230     PythonExtension* extension = extobj.extensionObject();
00231     if(! extension) {
00232         krosswarning("EXCEPTION in PythonExtension::toObject(): Failed to determinate PythonExtension object.");
00233         throw Py::Exception("Failed to determinate PythonExtension object.");
00234     }
00235     if(! extension->m_object) {
00236         krosswarning("EXCEPTION in PythonExtension::toObject(): Failed to convert the PythonExtension object into a Kross::Api::Object.");
00237         throw Py::Exception("Failed to convert the PythonExtension object into a Kross::Api::Object.");
00238     }
00239 
00240 #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
00241     krossdebug( "Kross::Python::PythonExtension::toObject(Py::Object) successfully converted into Kross::Api::Object." );
00242 #endif
00243     return extension->m_object;
00244 }
00245 
00246 const Py::Object PythonExtension::toPyObject(const QString& s)
00247 {
00248 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00249     krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QString)") );
00250 #endif
00251     return s.isNull() ? Py::String() : Py::String(s.latin1());
00252 }
00253 
00254 const Py::List PythonExtension::toPyObject(const QStringList& list)
00255 {
00256 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00257     krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QStringList)") );
00258 #endif
00259     Py::List l;
00260     for(QStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
00261         l.append(toPyObject(*it));
00262     return l;
00263 }
00264 
00265 const Py::Dict PythonExtension::toPyObject(const QMap<QString, QVariant>& map)
00266 {
00267 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00268     krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QMap<QString,QVariant>)") );
00269 #endif
00270     Py::Dict d;
00271     for(QMap<QString, QVariant>::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it)
00272         d.setItem(it.key().latin1(), toPyObject(it.data()));
00273     return d;
00274 }
00275 
00276 const Py::List PythonExtension::toPyObject(const QValueList<QVariant>& list)
00277 {
00278 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00279     krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QValueList<QVariant>)") );
00280 #endif
00281     Py::List l;
00282     for(QValueList<QVariant>::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
00283         l.append(toPyObject(*it));
00284     return l;
00285 }
00286 
00287 const Py::Object PythonExtension::toPyObject(const QVariant& variant)
00288 {
00289 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00290     krossdebug( QString("Kross::Python::PythonExtension::toPyObject(QVariant) typename='%1'").arg(variant.typeName()) );
00291 #endif
00292 
00293     switch(variant.type()) {
00294         case QVariant::Invalid:
00295             return Py::None();
00296         case QVariant::Bool:
00297             return Py::Int(variant.toBool());
00298         case QVariant::Int:
00299             return Py::Int(variant.toInt());
00300         case QVariant::UInt:
00301             return Py::Long((unsigned long)variant.toUInt());
00302         case QVariant::Double:
00303             return Py::Float(variant.toDouble());
00304         case QVariant::Date:
00305         case QVariant::Time:
00306         case QVariant::DateTime:
00307         case QVariant::ByteArray:
00308         case QVariant::BitArray:
00309         case QVariant::CString:
00310         case QVariant::String:
00311             return toPyObject(variant.toString());
00312         case QVariant::StringList:
00313             return toPyObject(variant.toStringList());
00314         case QVariant::Map:
00315             return toPyObject(variant.toMap());
00316         case QVariant::List:
00317             return toPyObject(variant.toList());
00318 
00319         // To handle following both cases is a bit difficult
00320         // cause Python doesn't spend an easy possibility
00321         // for such large numbers (TODO maybe BigInt?). So,
00322         // we risk overflows here, but well...
00323         case QVariant::LongLong: {
00324             Q_LLONG l = variant.toLongLong();
00325             //return (l < 0) ? Py::Long((long)l) : Py::Long((unsigned long)l);
00326             return Py::Long((long)l);
00327             //return Py::Long(PyLong_FromLong( (long)l ), true);
00328         } break;
00329         case QVariant::ULongLong: {
00330             return Py::Long((unsigned long)variant.toULongLong());
00331         } break;
00332 
00333         default: {
00334             krosswarning( QString("Kross::Python::PythonExtension::toPyObject(QVariant) Not possible to convert the QVariant type '%1' to a Py::Object.").arg(variant.typeName()) );
00335             return Py::None();
00336         }
00337     }
00338 }
00339 
00340 const Py::Object PythonExtension::toPyObject(Kross::Api::Object::Ptr object)
00341 {
00342     if(! object) {
00343 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00344         krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is NULL => Py::None");
00345 #endif
00346         return Py::None();
00347     }
00348 
00349     const QString classname = object->getClassName();
00350     if(classname == "Kross::Api::Variant") {
00351         QVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
00352 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00353         krossdebug( QString("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::Variant %1").arg(v.toString()) );
00354 #endif
00355         return toPyObject(v);
00356     }
00357 
00358     if(classname == "Kross::Api::List") {
00359 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00360         krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::List");
00361 #endif
00362         Py::List pylist;
00363         Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() );
00364         QValueList<Kross::Api::Object::Ptr> valuelist = list->getValue();
00365         for(QValueList<Kross::Api::Object::Ptr>::Iterator it = valuelist.begin(); it != valuelist.end(); ++it)
00366             pylist.append( toPyObject(*it) ); // recursive
00367         return pylist;
00368     }
00369 
00370     if(classname == "Kross::Api::Dict") {
00371 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00372         krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::Dict");
00373 #endif
00374         Py::Dict pydict;
00375         Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() );
00376         QMap<QString, Kross::Api::Object::Ptr> valuedict = dict->getValue();
00377         for(QMap<QString, Kross::Api::Object::Ptr>::Iterator it = valuedict.begin(); it != valuedict.end(); ++it) {
00378             const char* n = it.key().latin1();
00379             pydict[ n ] = toPyObject( it.data() ); // recursive
00380         }
00381         return pydict;
00382     }
00383 
00384 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00385     krossdebug( QString("Trying to handle PythonExtension::toPyObject(%1) as PythonExtension").arg(object->getClassName()) );
00386 #endif
00387     return Py::asObject( new PythonExtension(object) );
00388 }
00389 
00390 const Py::Tuple PythonExtension::toPyTuple(Kross::Api::List::Ptr list)
00391 {
00392 #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
00393     krossdebug( QString("Kross::Python::PythonExtension::toPyTuple(Kross::Api::List) name='%1'").arg(list ? list->getName() : "NULL") );
00394 #endif
00395     uint count = list ? list->count() : 0;
00396     Py::Tuple tuple(count);
00397     for(uint i = 0; i < count; i++)
00398         tuple.setItem(i, toPyObject(list->item(i)));
00399     return tuple;
00400 }
00401 
00402 PyObject* PythonExtension::proxyhandler(PyObject *_self_and_name_tuple, PyObject *args)
00403 {
00404     Py::Tuple tuple(_self_and_name_tuple);
00405     PythonExtension *self = static_cast<PythonExtension*>( tuple[0].ptr() );
00406     QString methodname = Py::String(tuple[1]).as_string().c_str();
00407 
00408     try {
00409         Kross::Api::List::Ptr arguments = toObject( Py::Tuple(args) );
00410 
00411 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
00412         krossdebug( QString("Kross::Python::PythonExtension::proxyhandler methodname='%1' arguments='%2'").arg(methodname).arg(arguments->toString()) );
00413 #endif
00414 
00415         Kross::Api::Callable* callable = dynamic_cast<Kross::Api::Callable*>(self->m_object.data());
00416         if(callable && callable->hasChild(methodname)) {
00417 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
00418             krossdebug( QString("Kross::Python::PythonExtension::proxyhandler methodname='%1' is a child object of '%2'.").arg(methodname).arg(self->m_object->getName()) );
00419 #endif
00420             Py::Object result = toPyObject( callable->getChild(methodname)->call(QString::null, arguments) );
00421             result.increment_reference_count();
00422             return result.ptr();
00423         }
00424 #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
00425         krossdebug( QString("Kross::Python::PythonExtension::proxyhandler try to call function with methodname '%1' in object '%2'.").arg(methodname).arg(self->m_object->getName()) );
00426 #endif
00427         Py::Object result = toPyObject( self->m_object->call(methodname, arguments) );
00428         result.increment_reference_count();
00429         return result.ptr();
00430     }
00431     catch(Py::Exception& e) {
00432         const QString err = Py::value(e).as_string().c_str();
00433         krosswarning( QString("Py::Exception in Kross::Python::PythonExtension::proxyhandler %1").arg(err) );
00434         //throw e;
00435     }
00436     catch(Kross::Api::Exception::Ptr e) {
00437         const QString err = e->toString();
00438         krosswarning( QString("Kross::Api::Exception in Kross::Python::PythonExtension::proxyhandler %1").arg(err) );
00439         // Don't throw here cause it will end in a crash deep in python. The
00440         // error is already handled anyway.
00441         //throw Py::Exception( (char*) e->toString().latin1() );
00442     }
00443 
00444     return Py_None;
00445 }
KDE Home | KDE Accessibility Home | Description of Access Keys