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

Generated on Thu Feb 9 17:59:10 2006 for Kross by  doxygen 1.4.6