lib

pythonextension.cpp
1/***************************************************************************
2 * pythonextension.cpp
3 * This file is part of the KDE project
4 * copyright (C)2004-2005 by Sebastian Sauer (mail@dipe.org)
5 *
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 ***************************************************************************/
19
20#include "pythonextension.h"
21#include "pythonobject.h"
22
23#include "../api/variant.h"
24#include "../api/dict.h"
25#include "../api/exception.h"
26
27using namespace Kross::Python;
28
31 , m_object(object)
32{
33#ifdef KROSS_PYTHON_EXTENSION_CTOR_DEBUG
34 krossdebug( TQString("Kross::Python::PythonExtension::Constructor objectname='%1' objectclass='%2'").arg(m_object->getName()).arg(m_object->getClassName()) );
35#endif
36
37 behaviors().name("KrossPythonExtension");
38 /*
39 behaviors().doc(
40 "The common KrossPythonExtension object enables passing "
41 "of Kross::Api::Object's from C/C++ to Python and "
42 "backwards in a transparent way."
43 );
44 */
45 behaviors().supportGetattr();
46
47 m_proxymethod = new Py::MethodDefExt<PythonExtension>(
48 "", // methodname, not needed cause we use the method only internaly.
49 0, // method that should handle the callback, not needed cause proxyhandler will handle it.
50 Py::method_varargs_call_handler_t( proxyhandler ), // callback handler
51 "" // documentation
52 );
53}
54
56{
57#ifdef KROSS_PYTHON_EXTENSION_DTOR_DEBUG
58 krossdebug( TQString("Kross::Python::PythonExtension::Destructor objectname='%1' objectclass='%2'").arg(m_object->getName()).arg(m_object->getClassName()) );
59#endif
60 delete m_proxymethod;
61}
62
63#if 0
64Py::Object PythonExtension::str()
65{
66 Kross::Api::Callable* callable = dynamic_cast< Kross::Api::Callable* >(m_object);
67 TQString s = callable ? callable->getName() : m_object->getClassName();
68 return toPyObject(s.isEmpty() ? : s);
69}
70
71Py::Object PythonExtension::repr()
72{
73 return toPyObject( m_object->toString() );
74}
75#endif
76
77Py::Object PythonExtension::getattr(const char* n)
78{
79#ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
80 krossdebug( TQString("Kross::Python::PythonExtension::getattr name='%1'").arg(n) );
81#endif
82
83 if(n[0] == '_') {
84 if(!strcmp(n, "__methods__")) {
85 Py::List methods;
86 TQStringList calls = m_object->getCalls();
87 for(TQStringList::Iterator it = calls.begin(); it != calls.end(); ++it) {
88#ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
89 krossdebug( TQString("Kross::Python::PythonExtension::getattr name='%1' callable='%2'").arg(n).arg(*it) );
90#endif
91 methods.append(Py::String( (*it).latin1() ));
92 }
93 return methods;
94 }
95
96 if(!strcmp(n, "__members__")) {
97 Py::List members;
98 Kross::Api::Callable* callable = dynamic_cast<Kross::Api::Callable*>(m_object.data());
99 if(callable) {
100 TQMap<TQString, Kross::Api::Object::Ptr> children = callable->getChildren();
101 TQMap<TQString, Kross::Api::Object::Ptr>::Iterator it( children.begin() );
102 for(; it != children.end(); ++it) {
103#ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
104 krossdebug( TQString("Kross::Python::PythonExtension::getattr n='%1' child='%2'").arg(n).arg(it.key()) );
105#endif
106 members.append(Py::String( it.key().latin1() ));
107 }
108 }
109 return members;
110 }
111
112 //if(n == "__dict__") { krosswarning( TQString("PythonExtension::getattr(%1) __dict__").arg(n) ); return Py::None(); }
113 //if(n == "__class__") { krosswarning( TQString("PythonExtension::getattr(%1) __class__").arg(n) ); return Py::None(); }
114
115#ifdef KROSS_PYTHON_EXTENSION_GETATTR_DEBUG
116 krossdebug( TQString("Kross::Python::PythonExtension::getattr name='%1' is a internal name.").arg(n) );
117#endif
118 return Py::PythonExtension<PythonExtension>::getattr_methods(n);
119 }
120
121 // Redirect the call to our static proxy method which will take care
122 // of handling the call.
123 Py::Tuple self(2);
124 self[0] = Py::Object(this);
125 self[1] = Py::String(n);
126 return Py::Object(PyCFunction_New( &m_proxymethod->ext_meth_def, self.ptr() ), true);
127}
128
129/*
130Py::Object PythonExtension::getattr_methods(const char* n)
131{
132#ifdef KROSS_PYTHON_EXTENSION_GETATTRMETHOD_DEBUG
133 krossdebug( TQString("PythonExtension::getattr_methods name=%1").arg(n) );
134#endif
135 return Py::PythonExtension<PythonExtension>::getattr_methods(n);
136}
137
138int PythonExtension::setattr(const char* name, const Py::Object& value)
139{
140#ifdef KROSS_PYTHON_EXTENSION_SETATTR_DEBUG
141 krossdebug( TQString("PythonExtension::setattr name=%1 value=%2").arg(name).arg(value.as_string().c_str()) );
142#endif
143 return Py::PythonExtension<PythonExtension>::setattr(name, value);
144}
145*/
146
147Kross::Api::List::Ptr PythonExtension::toObject(const Py::Tuple& tuple)
148{
149#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
150 krossdebug( TQString("Kross::Python::PythonExtension::toObject(Py::Tuple)") );
151#endif
152
153 TQValueList<Kross::Api::Object::Ptr> l;
154 uint size = tuple.size();
155 for(uint i = 0; i < size; i++)
156 l.append( toObject( tuple[i] ) );
157 return new Kross::Api::List(l);
158}
159
160Kross::Api::List::Ptr PythonExtension::toObject(const Py::List& list)
161{
162#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
163 krossdebug( TQString("Kross::Python::PythonExtension::toObject(Py::List)") );
164#endif
165
166 TQValueList<Kross::Api::Object::Ptr> l;
167 uint length = list.length();
168 for(uint i = 0; i < length; i++)
169 l.append( toObject( list[i] ) );
170 return new Kross::Api::List(l);
171}
172
173Kross::Api::Dict::Ptr PythonExtension::toObject(const Py::Dict& dict)
174{
175 TQMap<TQString, Kross::Api::Object::Ptr> map;
176 Py::List l = dict.keys();
177 uint length = l.length();
178 for(Py::List::size_type i = 0; i < length; ++i) {
179 const char* n = l[i].str().as_string().c_str();
180 map.replace(n, toObject( dict[n] ));
181 }
182 return new Kross::Api::Dict(map);
183}
184
185Kross::Api::Object::Ptr PythonExtension::toObject(const Py::Object& object)
186{
187#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
188 krossdebug( TQString("Kross::Python::PythonExtension::toObject(Py::Object) object='%1'").arg(object.as_string().c_str()) );
189#endif
190 if(object == Py::None())
191 return 0;
192 PyTypeObject *type = (PyTypeObject*) object.type().ptr();
193#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
194 krossdebug( TQString("Kross::Python::PythonExtension::toObject(Py::Object) type='%1'").arg(type->tp_name) );
195#endif
196 if(type == &PyInt_Type)
197 return new Kross::Api::Variant(int(Py::Int(object)));
198 if(type == &PyBool_Type)
199 return new Kross::Api::Variant(TQVariant(object.isTrue()));
200 if(type == &PyLong_Type)
201 return new Kross::Api::Variant(TQ_LLONG(long(Py::Long(object))));
202 if(type == &PyFloat_Type)
203 return new Kross::Api::Variant(double(Py::Float(object)));
204
205 if( PyType_IsSubtype(type,&PyString_Type) ) {
206#ifdef Py_USING_UNICODE
207 /* TODO
208 if(type == &PyUnicode_Type) {
209 Py::unicodestring u = Py::String(object).as_unicodestring();
210 std::string s;
211 std::copy(u.begin(), u.end(), std::back_inserter(s));
212 return new Kross::Api::Variant(s.c_str());
213 }
214 */
215#endif
216 return new Kross::Api::Variant(object.as_string().c_str());
217 }
218
219 if(type == &PyTuple_Type)
220 return toObject(Py::Tuple(object)).data();
221 if(type == &PyList_Type)
222 return toObject(Py::List(object)).data();
223 if(type == &PyDict_Type)
224 return toObject(Py::Dict(object.ptr())).data();
225
226 if(object.isInstance())
227 return new PythonObject(object);
228
229 Py::ExtensionObject<PythonExtension> extobj(object);
230 PythonExtension* extension = extobj.extensionObject();
231 if(! extension) {
232 krosswarning("EXCEPTION in PythonExtension::toObject(): Failed to determinate PythonExtension object.");
233 throw Py::Exception("Failed to determinate PythonExtension object.");
234 }
235 if(! extension->m_object) {
236 krosswarning("EXCEPTION in PythonExtension::toObject(): Failed to convert the PythonExtension object into a Kross::Api::Object.");
237 throw Py::Exception("Failed to convert the PythonExtension object into a Kross::Api::Object.");
238 }
239
240#ifdef KROSS_PYTHON_EXTENSION_TOOBJECT_DEBUG
241 krossdebug( "Kross::Python::PythonExtension::toObject(Py::Object) successfully converted into Kross::Api::Object." );
242#endif
243 return extension->m_object;
244}
245
246const Py::Object PythonExtension::toPyObject(const TQString& s)
247{
248#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
249 krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQString)") );
250#endif
251 return s.isNull() ? Py::String() : Py::String(s.latin1());
252}
253
254const Py::List PythonExtension::toPyObject(const TQStringList& list)
255{
256#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
257 krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQStringList)") );
258#endif
259 Py::List l;
260 for(TQStringList::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
261 l.append(toPyObject(*it));
262 return l;
263}
264
265const Py::Dict PythonExtension::toPyObject(const TQStringVariantMap& map)
266{
267#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
268 krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQStringVariantMap)") );
269#endif
270 Py::Dict d;
271 for(TQStringVariantMap::ConstIterator it = map.constBegin(); it != map.constEnd(); ++it)
272 d.setItem(it.key().latin1(), toPyObject(it.data()));
273 return d;
274}
275
276const Py::List PythonExtension::toPyObject(const TQValueList<TQVariant>& list)
277{
278#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
279 krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQValueList<TQVariant>)") );
280#endif
281 Py::List l;
282 for(TQValueList<TQVariant>::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it)
283 l.append(toPyObject(*it));
284 return l;
285}
286
287const Py::Object PythonExtension::toPyObject(const TQVariant& variant)
288{
289#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
290 krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(TQVariant) typename='%1'").arg(variant.typeName()) );
291#endif
292
293 switch(variant.type()) {
294 case TQVariant::Invalid:
295 return Py::None();
296 case TQVariant::Bool:
297 return Py::Int(variant.toBool());
298 case TQVariant::Int:
299 return Py::Int(variant.toInt());
300 case TQVariant::UInt:
301 return Py::Long((unsigned long)variant.toUInt());
302 case TQVariant::Double:
303 return Py::Float(variant.toDouble());
304 case TQVariant::Date:
305 case TQVariant::Time:
306 case TQVariant::DateTime:
307 case TQVariant::ByteArray:
308 case TQVariant::BitArray:
309 case TQVariant::CString:
310 case TQVariant::String:
311 return toPyObject(variant.toString());
312 case TQVariant::StringList:
313 return toPyObject(variant.toStringList());
314 case TQVariant::Map:
315 return toPyObject(variant.toMap());
316 case TQVariant::List:
317 return toPyObject(variant.toList());
318
319 // To handle following both cases is a bit difficult
320 // cause Python doesn't spend an easy possibility
321 // for such large numbers (TODO maybe BigInt?). So,
322 // we risk overflows here, but well...
323 case TQVariant::LongLong: {
324 TQ_LLONG l = variant.toLongLong();
325 //return (l < 0) ? Py::Long((long)l) : Py::Long((unsigned long)l);
326 return Py::Long((long)l);
327 //return Py::Long(PyLong_FromLong( (long)l ), true);
328 } break;
329 case TQVariant::ULongLong: {
330 return Py::Long((unsigned long)variant.toULongLong());
331 } break;
332
333 default: {
334 krosswarning( TQString("Kross::Python::PythonExtension::toPyObject(TQVariant) Not possible to convert the TQVariant type '%1' to a Py::Object.").arg(variant.typeName()) );
335 return Py::None();
336 }
337 }
338}
339
340const Py::Object PythonExtension::toPyObject(Kross::Api::Object::Ptr object)
341{
342 if(! object) {
343#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
344 krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is NULL => Py::None");
345#endif
346 return Py::None();
347 }
348
349 const TQString classname = object->getClassName();
350 if(classname == "Kross::Api::Variant") {
351 TQVariant v = static_cast<Kross::Api::Variant*>( object.data() )->getValue();
352#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
353 krossdebug( TQString("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::Variant %1").arg(v.toString()) );
354#endif
355 return toPyObject(v);
356 }
357
358 if(classname == "Kross::Api::List") {
359#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
360 krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::List");
361#endif
362 Py::List pylist;
363 Kross::Api::List* list = static_cast<Kross::Api::List*>( object.data() );
364 TQValueList<Kross::Api::Object::Ptr> valuelist = list->getValue();
365 for(TQValueList<Kross::Api::Object::Ptr>::Iterator it = valuelist.begin(); it != valuelist.end(); ++it)
366 pylist.append( toPyObject(*it) ); // recursive
367 return pylist;
368 }
369
370 if(classname == "Kross::Api::Dict") {
371#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
372 krossdebug("Kross::Python::PythonExtension::toPyObject(Kross::Api::Object) is Kross::Api::Dict");
373#endif
374 Py::Dict pydict;
375 Kross::Api::Dict* dict = static_cast<Kross::Api::Dict*>( object.data() );
376 TQMap<TQString, Kross::Api::Object::Ptr> valuedict = dict->getValue();
377 for(TQMap<TQString, Kross::Api::Object::Ptr>::Iterator it = valuedict.begin(); it != valuedict.end(); ++it) {
378 const char* n = it.key().latin1();
379 pydict[ n ] = toPyObject( it.data() ); // recursive
380 }
381 return pydict;
382 }
383
384#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
385 krossdebug( TQString("Trying to handle PythonExtension::toPyObject(%1) as PythonExtension").arg(object->getClassName()) );
386#endif
387 return Py::asObject( new PythonExtension(object) );
388}
389
390const Py::Tuple PythonExtension::toPyTuple(Kross::Api::List::Ptr list)
391{
392#ifdef KROSS_PYTHON_EXTENSION_TOPYOBJECT_DEBUG
393 krossdebug( TQString("Kross::Python::PythonExtension::toPyTuple(Kross::Api::List) name='%1'").arg(list ? list->getName() : "NULL") );
394#endif
395 uint count = list ? list->count() : 0;
396 Py::Tuple tuple(count);
397 for(uint i = 0; i < count; i++)
398 tuple.setItem(i, toPyObject(list->item(i)));
399 return tuple;
400}
401
402PyObject* PythonExtension::proxyhandler(PyObject *_self_and_name_tuple, PyObject *args)
403{
404 Py::Tuple tuple(_self_and_name_tuple);
405 PythonExtension *self = static_cast<PythonExtension*>( tuple[0].ptr() );
406 TQString methodname = Py::String(tuple[1]).as_string().c_str();
407
408 try {
409 Kross::Api::List::Ptr arguments = toObject( Py::Tuple(args) );
410
411#ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
412 krossdebug( TQString("Kross::Python::PythonExtension::proxyhandler methodname='%1' arguments='%2'").arg(methodname).arg(arguments->toString()) );
413#endif
414
415 Kross::Api::Callable* callable = dynamic_cast<Kross::Api::Callable*>(self->m_object.data());
416 if(callable && callable->hasChild(methodname)) {
417#ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
418 krossdebug( TQString("Kross::Python::PythonExtension::proxyhandler methodname='%1' is a child object of '%2'.").arg(methodname).arg(self->m_object->getName()) );
419#endif
420 Py::Object result = toPyObject( callable->getChild(methodname)->call(TQString(), arguments) );
421 result.increment_reference_count();
422 return result.ptr();
423 }
424#ifdef KROSS_PYTHON_EXTENSION_CALL_DEBUG
425 krossdebug( TQString("Kross::Python::PythonExtension::proxyhandler try to call function with methodname '%1' in object '%2'.").arg(methodname).arg(self->m_object->getName()) );
426#endif
427 Py::Object result = toPyObject( self->m_object->call(methodname, arguments) );
428 result.increment_reference_count();
429 return result.ptr();
430 }
431 catch(Py::Exception& e) {
432 const TQString err = Py::value(e).as_string().c_str();
433 krosswarning( TQString("Py::Exception in Kross::Python::PythonExtension::proxyhandler %1").arg(err) );
434 //throw e;
435 }
437 const TQString err = e->toString();
438 krosswarning( TQString("Kross::Api::Exception in Kross::Python::PythonExtension::proxyhandler %1").arg(err) );
439 // Don't throw here cause it will end in a crash deep in python. The
440 // error is already handled anyway.
441 //throw Py::Exception( (char*) e->toString().latin1() );
442 }
443
444 return Py_None;
445}
Base class for callable objects.
Definition callable.h:42
TQMap< TQString, Object::Ptr > getChildren() const
Return all children.
Definition callable.cpp:57
bool hasChild(const TQString &name) const
Returns if the defined child is avaible.
Definition callable.cpp:47
const TQString getName() const
Definition callable.cpp:37
Object::Ptr getChild(const TQString &name) const
Return the defined child or NULL if there is no such object with that name avaible.
Definition callable.cpp:52
TDESharedPtr< Exception > Ptr
Shared pointer to implement reference-counting.
Definition exception.h:45
The List class implementates Value to handle lists and collections.
Definition list.h:39
TDESharedPtr< List > Ptr
Shared pointer to implement reference-counting.
Definition list.h:46
TDESharedPtr< Object > Ptr
Shared pointer to implement reference-counting.
Definition object.h:56
V & getValue()
Return the value.
Definition value.h:64
virtual ~PythonExtension()
Destructor.
virtual Py::Object getattr(const char *name)
Overloaded method to handle attribute calls from within python.
PythonExtension(Kross::Api::Object::Ptr object)
Constructor.
@ String
string
Definition property.h:71
The Python plugin for the Kross scripting framework.
KOFFICE_EXPORT void krossdebug(const TQString &s)
Debugging function.
KOFFICE_EXPORT void krosswarning(const TQString &s)
Warning function.