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