/*************************************************************************** * pythonextension.cpp * This file is part of the KDE project * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * You should have received a copy of the GNU Library General Public License * along with this program; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. ***************************************************************************/ #include "pythonextension.h" #include "pythonobject.h" #include "../api/variant.h" #include "../api/dict.h" #include "../api/exception.h" using namespace Kross::Python; PythonExtension::PythonExtension(Kross::Api::Object::Ptr object) : Py::PythonExtension() , m_object(object) { #ifdef KROSS_PYTHON_EXTENSION_CTOR_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::Constructor objectname='%1' objectclass='%2'").arg(m_object->getName()).arg(m_object->getClassName()) ); #endif behaviors().name("KrossPythonExtension"); /* behaviors().doc( "The common KrossPythonExtension object enables passing " "of Kross::Api::Object's from C/C++ to Python and " "backwards in a transparent way." ); */ behaviors().supportGetattr(); m_proxymethod = new Py::MethodDefExt( "", // methodname, not needed cause we use the method only internaly. 0, // method that should handle the callback, not needed cause proxyhandler will handle it. Py::method_varargs_call_handler_t( proxyhandler ), // callback handler "" // documentation ); } PythonExtension::~PythonExtension() { #ifdef KROSS_PYTHON_EXTENSION_DTOR_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::Destructor objectname='%1' objectclass='%2'").arg(m_object->getName()).arg(m_object->getClassName()) ); #endif delete m_proxymethod; } #if 0 Py::Object PythonExtension::str() { Kross::Api::Callable* callable = dynamic_cast< Kross::Api::Callable* >(m_object); TQString s = callable ? callable->getName() : m_object->getClassName(); return toPyObject(s.isEmpty() ? : s); } Py::Object PythonExtension::repr() { return toPyObject( m_object->toString() ); } #endif Py::Object PythonExtension::getattr(const char* n) { #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::getattr name='%1'").arg(n) ); #endif if(n[0] == '_') { if(!strcmp(n, "__methods__")) { Py::List methods; TQStringList calls = m_object->getCalls(); for(TQStringList::Iterator it = calls.begin(); it != calls.end(); ++it) { #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::getattr name='%1' callable='%2'").arg(n).arg(*it) ); #endif methods.append(Py::String( (*it).latin1() )); } return methods; } if(!strcmp(n, "__members__")) { Py::List members; Kross::Api::Callable* callable = dynamic_cast(m_object.data()); if(callable) { TQMap children = callable->getChildren(); TQMap::Iterator it( children.begin() ); for(; it != children.end(); ++it) { #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::getattr n='%1' child='%2'").arg(n).arg(it.key()) ); #endif members.append(Py::String( it.key().latin1() )); } } return members; } //if(n == "__dict__") { krosswarning( TQString("PythonExtension::getattr(%1) __dict__").arg(n) ); return Py::None(); } //if(n == "__class__") { krosswarning( TQString("PythonExtension::getattr(%1) __class__").arg(n) ); return Py::None(); } #ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::getattr name='%1' is a internal name.").arg(n) ); #endif return Py::PythonExtension::getattr_methods(n); } // Redirect the call to our static proxy method which will take care // of handling the call. Py::Tuple self(2); self[0] = Py::Object(this); self[1] = Py::String(n); return Py::Object(PyCFunction_New( &m_proxymethod->ext_meth_def, self.ptr() ), true); } /* Py::Object PythonExtension::getattr_methods(const char* n) { #ifdef KROSS_PYTHON_EXTENSION_GETATTRMETHOD_DEBUG krossdebug( TQString("PythonExtension::getattr_methods name=%1").arg(n) ); #endif return Py::PythonExtension::getattr_methods(n); } int PythonExtension::setattr(const char* name, const Py::Object& value) { #ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG krossdebug( TQString("PythonExtension::setattr name=%1 value=%2").arg(name).arg(value.as_string().c_str()) ); #endif return Py::PythonExtension::setattr(name, value); } */ Kross::Api::List::Ptr PythonExtension::toObject(const Py::Tuple& tuple) { #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toObject(Py::Tuple)") ); #endif TQValueList l; uint size = tuple.size(); for(uint i = 0; i < size; i++) l.append( toObject( tuple[i] ) ); return new Kross::Api::List(l); } Kross::Api::List::Ptr PythonExtension::toObject(const Py::List& list) { #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toObject(Py::List)") ); #endif TQValueList l; uint length = list.length(); for(uint i = 0; i < length; i++) l.append( toObject( list[i] ) ); return new Kross::Api::List(l); } Kross::Api::Dict::Ptr PythonExtension::toObject(const Py::Dict& dict) { TQMap map; Py::List l = dict.keys(); uint length = l.length(); for(Py::List::size_type i = 0; i < length; ++i) { const char* n = l[i].str().as_string().c_str(); map.replace(n, toObject( dict[n] )); } return new Kross::Api::Dict(map); } Kross::Api::Object::Ptr PythonExtension::toObject(const Py::Object& object) { #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toObject(Py::Object) object='%1'").arg(object.as_string().c_str()) ); #endif if(object == Py::None()) return 0; PyTypeObject *type = (PyTypeObject*) object.type().ptr(); #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toObject(Py::Object) type='%1'").arg(type->tp_name) ); #endif if(type == &PyInt_Type) return new Kross::Api::Variant(int(Py::Int(object))); if(type == &PyBool_Type) return new Kross::Api::Variant(TQVariant(object.isTrue(),0)); if(type == &PyLong_Type) return new Kross::Api::Variant(TQ_LLONG(long(Py::Long(object)))); if(type == &PyFloat_Type) return new Kross::Api::Variant(double(Py::Float(object))); if( PyType_IsSubtype(type,&PyString_Type) ) { #ifdef Py_USING_UNICODE /* TODO if(type == &PyUnicode_Type) { Py::unicodestring u = Py::String(object).as_unicodestring(); std::string s; std::copy(u.begin(), u.end(), std::back_inserter(s)); return new Kross::Api::Variant(s.c_str()); } */ #endif return new Kross::Api::Variant(object.as_string().c_str()); } if(type == &PyTuple_Type) return toObject(Py::Tuple(object)).data(); if(type == &PyList_Type) return toObject(Py::List(object)).data(); if(type == &PyDict_Type) return toObject(Py::Dict(object.ptr())).data(); if(object.isInstance()) return new PythonObject(object); Py::ExtensionObject extobj(object); PythonExtension* extension = extobj.extensionObject(); if(! extension) { krosswarning("EXCEPTION in PythonExtension::toObject(): Failed to determinate PythonExtension object."); throw Py::Exception("Failed to determinate PythonExtension object."); } if(! extension->m_object) { krosswarning("EXCEPTION in PythonExtension::toObject(): Failed to convert the PythonExtension object into a Kross::Api::Object."); throw Py::Exception("Failed to convert the PythonExtension object into a Kross::Api::Object."); } #ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG krossdebug( "Kross::Python::PythonExtension::toObject(Py::Object) successfully converted into Kross::Api::Object." ); #endif return extension->m_object; } const Py::Object PythonExtension::toPyObject(const TQString& s) { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQString)") ); #endif return s.isNull() ? Py::String() : Py::String(s.latin1()); } const Py::List PythonExtension::toPyObject(const TQStringList& list) { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQStringList)") ); #endif Py::List l; for(TQStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) l.append(toPyObject(*it)); return l; } const Py::Dict PythonExtension::toPyObject(const TQMap& map) { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQMap)") ); #endif Py::Dict d; for(TQMap::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it) d.setItem(it.key().latin1(), toPyObject(it.data())); return d; } const Py::List PythonExtension::toPyObject(const TQValueList& list) { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQValueList)") ); #endif Py::List l; for(TQValueList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) l.append(toPyObject(*it)); return l; } const Py::Object PythonExtension::toPyObject(const TQVariant& variant) { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQVariant) typename='%1'").arg(variant.typeName()) ); #endif switch(variant.type()) { case TQVariant::Invalid: return Py::None(); case TQVariant::Bool: return Py::Int(variant.toBool()); case TQVariant::Int: return Py::Int(variant.toInt()); case TQVariant::UInt: return Py::Long((unsigned long)variant.toUInt()); case TQVariant::Double: return Py::Float(variant.toDouble()); case TQVariant::Date: case TQVariant::Time: case TQVariant::DateTime: case TQVariant::ByteArray: case TQVariant::BitArray: case TQVariant::CString: case TQVariant::String: return toPyObject(variant.toString()); case TQVariant::StringList: return toPyObject(variant.toStringList()); case TQVariant::Map: return toPyObject(variant.toMap()); case TQVariant::List: return toPyObject(variant.toList()); // To handle following both cases is a bit difficult // cause Python doesn't spend an easy possibility // for such large numbers (TODO maybe BigInt?). So, // we risk overflows here, but well... case TQVariant::LongLong: { TQ_LLONG l = variant.toLongLong(); //return (l < 0) ? Py::Long((long)l) : Py::Long((unsigned long)l); return Py::Long((long)l); //return Py::Long(PyLong_FromLong( (long)l ), true); } break; case TQVariant::ULongLong: { return Py::Long((unsigned long)variant.toULongLong()); } break; default: { krosswarning( TQString("Kross::Python::PythonExtension::toPyObject(TQVariant) Not possible to convert the TQVariant type '%1' to a Py::Object.").arg(variant.typeName()) ); return Py::None(); } } } const Py::Object PythonExtension::toPyObject(Kross::Api::Object::Ptr object) { if(! object) { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is NULL => Py::None"); #endif return Py::None(); } const TQString classname = object->getClassName(); if(classname == "Kross::Api::Variant") { TQVariant v = static_cast( object.data() )->getValue(); #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::Variant %1").arg(v.toString()) ); #endif return toPyObject(v); } if(classname == "Kross::Api::List") { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::List"); #endif Py::List pylist; Kross::Api::List* list = static_cast( object.data() ); TQValueList valuelist = list->getValue(); for(TQValueList::Iterator it = valuelist.begin(); it != valuelist.end(); ++it) pylist.append( toPyObject(*it) ); // recursive return pylist; } if(classname == "Kross::Api::Dict") { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::Dict"); #endif Py::Dict pydict; Kross::Api::Dict* dict = static_cast( object.data() ); TQMap valuedict = dict->getValue(); for(TQMap::Iterator it = valuedict.begin(); it != valuedict.end(); ++it) { const char* n = it.key().latin1(); pydict[ n ] = toPyObject( it.data() ); // recursive } return pydict; } #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug( TQString("Trying to handle PythonExtension::toPyObject(%1) as PythonExtension").arg(object->getClassName()) ); #endif return Py::asObject( new PythonExtension(object) ); } const Py::Tuple PythonExtension::toPyTuple(Kross::Api::List::Ptr list) { #ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::toPyTuple(Kross::Api::List) name='%1'").arg(list ? list->getName() : "NULL") ); #endif uint count = list ? list->count() : 0; Py::Tuple tuple(count); for(uint i = 0; i < count; i++) tuple.setItem(i, toPyObject(list->item(i))); return tuple; } PyObject* PythonExtension::proxyhandler(PyObject *_self_and_name_tuple, PyObject *args) { Py::Tuple tuple(_self_and_name_tuple); PythonExtension *self = static_cast( tuple[0].ptr() ); TQString methodname = Py::String(tuple[1]).as_string().c_str(); try { Kross::Api::List::Ptr arguments = toObject( Py::Tuple(args) ); #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::proxyhandler methodname='%1' arguments='%2'").arg(methodname).arg(arguments->toString()) ); #endif Kross::Api::Callable* callable = dynamic_cast(self->m_object.data()); if(callable && callable->hasChild(methodname)) { #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::proxyhandler methodname='%1' is a child object of '%2'.").arg(methodname).arg(self->m_object->getName()) ); #endif Py::Object result = toPyObject( callable->getChild(methodname)->call(TQString(), arguments) ); result.increment_reference_count(); return result.ptr(); } #ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG krossdebug( TQString("Kross::Python::PythonExtension::proxyhandler try to call function with methodname '%1' in object '%2'.").arg(methodname).arg(self->m_object->getName()) ); #endif Py::Object result = toPyObject( self->m_object->call(methodname, arguments) ); result.increment_reference_count(); return result.ptr(); } catch(Py::Exception& e) { const TQString err = Py::value(e).as_string().c_str(); krosswarning( TQString("Py::Exception in Kross::Python::PythonExtension::proxyhandler %1").arg(err) ); //throw e; } catch(Kross::Api::Exception::Ptr e) { const TQString err = e->toString(); krosswarning( TQString("Kross::Api::Exception in Kross::Python::PythonExtension::proxyhandler %1").arg(err) ); // Don't throw here cause it will end in a crash deep in python. The // error is already handled anyway. //throw Py::Exception( (char*) e->toString().latin1() ); } return Py_None; }