/*************************************************************************** * Copyright (C) 2003 by Julian Rockey (linux@jrockey.com) * * Original code by Torben Weis * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include "pcop.h" #include #include #include #include #include #include #include #include #include #include "marshaller.h" #include "importedmodules.h" namespace PythonDCOP { PCOPObject::PCOPObject(PyObject *py_obj) : DCOPObject(), m_py_obj(py_obj) { m_methods.setAutoDelete(true); } PCOPObject::PCOPObject(PyObject *py_obj, const char *objid) : DCOPObject(TQCString(objid)), m_py_obj(py_obj) { m_methods.setAutoDelete(true); } PCOPObject::~PCOPObject() { } bool PCOPObject::process(const TQCString &fun, const TQByteArray &data, TQCString& replyType, TQByteArray &replyData) { bool result = py_process(fun,data,replyType,replyData); if (PyErr_Occurred()) { kdDebug(70001) << "Error! About to print..." << endl; PyErr_Print(); kdDebug(70001) << "About to clear..." << endl; PyErr_Clear(); kdDebug(70001) << "Error handled." << endl; } return result; } bool PCOPObject::py_process(const TQCString &fun, const TQByteArray &data, TQCString& replyType, TQByteArray &replyData) { kdDebug(70001) << "PCOPObject::process - fun=" << fun << " replyType=" << replyType << endl; PCOPMethod *meth = matchMethod(fun); if (!meth) { kdDebug(70001) << "Could not match method name" << endl; } if (meth) { kdDebug(70001) << "m_py_obj=" << m_py_obj << " meth->name=" << meth->name() << " meth->name.data=" << meth->name().data() << endl; if (meth->name().isNull()) { kdDebug(70001) << "meth name is null" << endl; return false; } // if (!PyObject_HasAttrString(m_py_obj, meth->name().data())) { // kdDebug(70001) << "Method registered, but no python method found" << endl; // return false; // } TQDataStream str_arg(data, IO_ReadOnly); PyObject *args = PyTuple_New( meth->paramCount() ); for(int c=0;cparamCount();c++) { kdDebug(70001) << "Demarshalling type: " << meth->param(c)->signature() << endl; PyObject *arg = meth->param(c)->demarshal(str_arg); if (!arg) { kdDebug(70001) << "Failed to demarshall an argument" << endl; return false; } PyTuple_SetItem(args, c, arg ); } kdDebug(70001) << "args is " << PyTuple_Size(args) << " long" << endl; // PyObject *method = PyObject_GetAttrString(m_py_obj, meth->name().data() ); PyObject *method = meth->pythonMethod(); if (!PyCallable_Check(method)) { kdDebug(70001) << "Expected a callable object, but didn't get one!" << endl; return false; } // PyObject *function = PyMethod_Function(method); // PyObject *self = PyMethod_Self(method); // Py_INCREF(self); // PyTuple_SetItem(args, 0, self ); // PyObject *result = PyObject_CallObject(function, args); // Py_DECREF(method); if (PyMethod_Self(method)==NULL) kdDebug(70001) << "Warning: self is null!" << endl; kdDebug(70001) << "About to call object.." << endl; PyObject *result = PyObject_CallObject(method, args); kdDebug(70001) << "Finished calling object." << endl; if (result) { replyType = meth->type()->signature(); PCOPType repl(replyType); if (repl.isMarshallable(result)) { TQDataStream str_repl(replyData, IO_WriteOnly); repl.marshal(result,str_repl); Py_DECREF(result); return true; } else { Py_DECREF(result); kdDebug(70001) << "Result of python method was not marshallable into " << replyType << endl; return false; } } else { kdDebug(70001) << "null result from python method call" << endl; return false; } } return DCOPObject::process(fun,data,replyType,replyData); } bool PCOPObject::setMethodList(TQAsciiDict meth_list) { bool ok = true; for(TQAsciiDictIterator it(meth_list); it.current(); ++it) { PCOPMethod *meth = NULL; if (ok) { meth = new PCOPMethod(TQCString(it.currentKey())); if (!meth || !meth->setPythonMethod(it.current())) { if (meth) delete meth; meth=NULL; m_methods.clear(); ok=false; } } // Py_DECREF(it.current()); if (meth) m_methods.insert(meth->signature(),meth); } return ok; } QCStringList PCOPObject::functions() { QCStringList funcs = DCOPObject::functions(); for(TQAsciiDictIterator it(m_methods); it.current(); ++it) { PCOPMethod *meth = it.current(); TQCString func = meth->type()->signature(); func += ' '; func += meth->signature(); funcs << func; } return funcs; } /** * For testing */ PyObject *PCOPObject::methodList() { PyObject *result = PyList_New(m_methods.count()); int c=0; for(TQAsciiDictIterator it(m_methods); it.current(); ++it, ++c) { PyObject *tuple = PyTuple_New(2); PyList_SetItem(result, c, tuple); PyTuple_SetItem(tuple, 0, PyBytes_FromString(it.currentKey() ) ); PyTuple_SetItem(tuple, 1, it.current()->pythonMethod() ); } return result; } PCOPMethod *PCOPObject::matchMethod(const TQCString &fun) { return m_methods.find(fun); } PCOPType::PCOPType( const TQCString& type ) { m_leftType = NULL; m_rightType = NULL; int pos = type.find( '<' ); if ( pos == -1 ) { m_type = type; return; } int pos2 = type.findRev( '>' ); if ( pos2 == -1 ) return; m_type = type.left( pos ); // There may be no more than 2 types in the bracket int komma = type.find( ',', pos + 1 ); if ( komma == -1 ) { m_leftType = new PCOPType( type.mid( pos + 1, pos2 - pos - 1 ) ); } else { m_leftType = new PCOPType( type.mid( pos + 1, komma - pos - 1 ) ); m_rightType = new PCOPType( type.mid( komma + 1, pos2 - komma - 1 ) ); } } PCOPType::~PCOPType() { if (m_leftType) delete m_leftType; if (m_rightType) delete m_rightType; } TQCString PCOPType::signature() const { TQCString str = m_type; if ( m_leftType ) { str += "<"; str += m_leftType->signature(); if ( m_rightType ) { str += ","; str += m_rightType->signature(); } str += ">"; } return str; } bool PCOPType::marshal( PyObject* obj, TQDataStream& str ) const { return Marshaller::instance()->marshal(*this, obj, str); } bool PCOPType::isMarshallable( PyObject *obj ) const { return Marshaller::instance()->canMarshal(*this, obj); } PyObject* PCOPType::demarshal( TQDataStream& str ) const { return Marshaller::instance()->demarshal(*this, str); } PCOPMethod::PCOPMethod( const TQCString& signature ) : m_py_method(NULL) { m_type = 0; m_params.setAutoDelete( TRUE ); // Find the space that separates the type from the name int k = signature.find( ' ' ); if ( k == -1 ) return; // Create the return type from the string m_type = new PCOPType( signature.left( k ) ); // Find the brackets int i = signature.find( '(' ); if ( i == -1 ) return; int j = signature.find( ')' ); if ( j == -1 ) return; // Extract the name m_name = signature.mid( k + 1, i - k - 1 ); // Strip the parameters TQCString p = signature.mid( i + 1, j - i - 1 ).stripWhiteSpace(); if ( !p.isEmpty() ) { // Make the algorithm terminate p += ","; // Iterate over the parameters int level = 0; int start = 0; int len = p.length(); for( int i = 0; i < len; ++i ) { // Found a comma? Then we reached the end of a parameter if ( p[i] == ',' && level == 0 ) { // Find the space that separates name from type. int space = p.find( ' ', start ); if ( space == -1 || space > i ) // unnamed parameter space = i; PCOPType* type = new PCOPType( p.mid( start, space - start ) ); m_params.append( type ); // Start of the next parameter start = i + 1; } else if ( p[i] == '<' ) ++level; else if ( p[i] == '>' ) --level; } } m_signature = m_name; m_signature += "("; TQPtrListIterator it( m_params ); for( ; it.current(); ++it ) { if ( !it.atFirst() ) m_signature += ','; m_signature += it.current()->signature(); } m_signature += ")"; } PCOPMethod::~PCOPMethod() { delete m_type; if (m_py_method) { Py_DECREF(m_py_method); } } bool PCOPMethod::setPythonMethod(PyObject *method) { if (method && PyMethod_Check(method)) { if (m_py_method) { Py_DECREF(m_py_method); } m_py_method = method; Py_INCREF(m_py_method); return true; } return false; } int PCOPMethod::paramCount() const { return m_params.count(); } PCOPType* PCOPMethod::param( int i ) { return m_params.at( i ); } const PCOPType* PCOPMethod::param( int i ) const { return ((PCOPMethod*)this)->m_params.at( i ); } PCOPClass::PCOPClass( const QCStringList& methods ) { m_methods.setAutoDelete( true ); QCStringList::ConstIterator it = methods.begin(); for( ; it != methods.end(); ++it ) { PCOPMethod* m = new PCOPMethod( *it ); m_methods.insert( m->m_name, m ); } } PCOPClass::~PCOPClass() { } const PCOPMethod* PCOPClass::method( const TQCString &name, PyObject *argTuple ) { if ( !argTuple ) return m_methods[ name ]; TQAsciiDictIterator it( m_methods ); for (; it.current(); ++it ) if ( it.currentKey() == name && it.current()->paramCount() == PyTuple_Size( argTuple ) ) { // ok, name and argument count match, now check if the python // can be marshalled to the qt/dcop type PCOPMethod *m = it.current(); bool fullMatch = true; for ( int i = 0; i < m->paramCount(); ++i ) if ( !m->param( i )->isMarshallable( PyTuple_GetItem( argTuple, i ) ) ) { fullMatch = false; break; } if ( fullMatch ) return m; } return 0; } // Client Client::Client() : m_dcop(NULL), m_qapp(NULL) { ImportedModules::setInstance( new ImportedModules ); int argc = 0; char **argv = NULL; m_qapp = new TQApplication(argc,argv,false); } Client::~Client() { // if (m_qapp) delete m_qapp; if (m_dcop) delete m_dcop; } void Client::processEvents() { if (m_qapp) { // kdDebug(70001) << "Processing events..." << endl; m_qapp->processEvents(); } } DCOPClient *Client::dcop() { if ( !m_dcop ) { m_dcop = new DCOPClient; if ( !m_dcop->attach() ) kdWarning(70001) << "Could not attach to DCOP server"; } return m_dcop; } Client *Client::instance() { return s_instance; } Client *Client::s_instance = new Client; //////////////////////////////////////////////// // // Methods accessed by python // //////////////////////////////////////////////// PyObject* dcop_call( PyObject* /*self*/, PyObject* args ) { char *arg1; char *arg2; char *arg3; PyObject* tuple; if ( !PyArg_ParseTuple( args, (char*)"sssO", &arg1, &arg2, &arg3, &tuple ) ) return NULL; if ( !PyTuple_Check( tuple ) ) return NULL; TQByteArray replyData; TQCString replyType; TQByteArray data; TQDataStream params( data, IO_WriteOnly ); TQCString appname( arg1 ); TQCString objname( arg2 ); TQCString funcname( arg3 ); // // Remove escape characters // if ( objname[0] == '_' ) objname = objname.mid( 1 ); if ( funcname[0] == '_' ) funcname = funcname.mid( 1 ); DCOPClient* dcop = Client::instance()->dcop(); // // Determine which functions are available. // bool ok = false; QCStringList funcs = dcop->remoteFunctions( appname, objname, &ok ); if ( !ok ) { PyErr_SetString( PyExc_RuntimeError, "Object is not accessible." ); return NULL; } // for ( QCStringList::Iterator it = funcs.begin(); it != funcs.end(); ++it ) { // tqDebug( "%s", (*it).data() ); // } // // Create a parse tree and search for the method // // ### Check wether that is sane PCOPClass c( funcs ); // tqDebug("Parsing done."); // Does the requested method exist ? const PCOPMethod* m = c.method( funcname, tuple ); if ( !m ) { PyErr_SetString( PyExc_RuntimeError, "DCOP: Unknown method." ); return NULL; } TQCString signature = m->signature(); kdDebug(70001) << "The signature is " << signature.data() << endl; kdDebug(70001) << "The method takes " << m->paramCount() << " parameters" << endl; // // Marshal the parameters. // int param_count = m->paramCount(); for( int p = 0; p < param_count; ++p ) { PyObject* o = PyTuple_GetItem( tuple, p ); // #### Check for errors if ( !m->param( p )->marshal( o, params ) ) { kdDebug(70001) << "QD: Could not marshal paramater %i" << p << endl; PyErr_SetString( PyExc_RuntimeError, "DCOP: marshaling failed" ); return NULL; } } kdDebug(70001) << "Calling " << appname.data() << " " << objname.data() << " " << signature.data() << endl; // ASSERT( Client::instance()->dcop() != 0 ); ASSERT(dcop); if ( !dcop->call( appname, objname, signature, data, replyType, replyData ) ) { PyErr_SetString( PyExc_RuntimeError, "DCOP: call failed" ); return NULL; } kdDebug(70001) << "The return type is " << replyType.data() << endl; // // Now decode the return type. // // ### Check wether that was sane PCOPType type( replyType ); TQDataStream reply(replyData, IO_ReadOnly); return type.demarshal( reply ); } PyObject* application_list( PyObject */*self*/, PyObject */*args*/ ) { QCStringList apps = Client::instance()->dcop()->registeredApplications(); PyObject *l = PyList_New( apps.count() ); QCStringList::ConstIterator it = apps.begin(); QCStringList::ConstIterator end = apps.end(); unsigned int i = 0; for (; it != end; ++it, i++ ) PyList_SetItem( l, i, PyBytes_FromString( (*it).data() ) ); return l; } PyObject *object_list( PyObject */*self*/, PyObject *args) { const char *app; if (PyArg_ParseTuple(args, (char*)"s", &app)) { QCStringList objects = Client::instance()->dcop()->remoteObjects(TQCString(app)); return make_py_list(objects); } return NULL; } PyObject *method_list( PyObject */*self*/, PyObject *args) { const char *app, *obj; if (PyArg_ParseTuple(args, (char*)"ss", &app, &obj)) { QCStringList methods = Client::instance()->dcop()->remoteFunctions(TQCString(app), TQCString(obj) ); return make_py_list(methods); } return NULL; } PyObject *register_as( PyObject */*self*/, PyObject *args) { const char *appid; int add_pid = 1; if (PyArg_ParseTuple(args, (char*)"s|i", &appid, &add_pid)) { TQCString actual_appid = Client::instance()->dcop()->registerAs(TQCString(appid), add_pid!=0); return PyBytes_FromString(actual_appid.data()); } return NULL; } PyObject *create_dcop_object( PyObject */*self*/, PyObject *args) { PyObject *py_dcop_object; const char *objid = NULL; if (PyArg_ParseTuple(args, (char*)"O|s", &py_dcop_object, &objid)) { Py_INCREF(py_dcop_object); PCOPObject *obj = objid ? new PCOPObject(py_dcop_object, objid) : new PCOPObject(py_dcop_object); return PyCapsule_New( (void*)obj, NULL, (PyCapsule_Destructor)delete_dcop_object ); } return NULL; } /** * pcop.set_method_list( , ) * where is a list of tuples * [ ('method signature', python method), ... ] */ PyObject *set_method_list( PyObject */*self*/, PyObject *args) { PyObject *c_obj; PyObject *method_list; if (PyArg_ParseTuple(args, (char*)"OO", &c_obj, &method_list) && PyCapsule_CheckExact(c_obj) && PyList_Check(method_list)) { // extract each tuple from the list, aborting if any is invalid TQAsciiDict meth_list; int size = PyList_Size(method_list); for(int c=0;csetMethodList(meth_list)) return NULL; } Py_INCREF(Py_None); return Py_None; } return NULL; } PyObject *get_method_list(PyObject */*self*/, PyObject *args) { PyObject *c_obj; if (PyArg_ParseTuple(args, (char*)"O", &c_obj)) { if (PyCapsule_CheckExact(c_obj)) { PCOPObject *obj = (PCOPObject*)PyCapsule_GetPointer(c_obj, NULL); return obj->methodList(); } } return NULL; } PyObject *connect_DCOP_Signal( PyObject */*self*/, PyObject *args) { const char *sender; const char *senderObj; const char *signal; const char *receiverObj; const char *slot; int volint = 0; if (PyArg_ParseTuple(args, (char*)"sssss|i", &sender, &senderObj, &signal, &receiverObj, &slot, &volint)) { bool success = Client::instance()->dcop()->connectDCOPSignal(TQCString(sender), TQCString(senderObj), TQCString(signal), TQCString(receiverObj), TQCString(slot), (volint == 1)?true:false); return Py_BuildValue("i", success?1:0); } return NULL; } PyObject *disconnect_DCOP_Signal( PyObject *self, PyObject *args) { const char *sender; const char *senderObj; const char *signal; const char *receiverObj; const char *slot; if (PyArg_ParseTuple(args, (char*)"sssss", &sender, &senderObj, &signal, &receiverObj, &slot)) { bool success = Client::instance()->dcop()->disconnectDCOPSignal(TQCString(sender), TQCString(senderObj), TQCString(signal), TQCString(receiverObj), TQCString(slot)); return Py_BuildValue("i", success?1:0); } return NULL; } void delete_dcop_object(void *vp) { if (vp) { PCOPObject *obj = (PCOPObject*)vp; delete obj; } } PyObject *process_events( PyObject */*self*/, PyObject */*args*/) { Client::instance()->processEvents(); Py_INCREF(Py_None); return Py_None; } // helpers PyObject *make_py_list( const QCStringList &qt_list) { PyObject *l = PyList_New(qt_list.count()); uint c=0; for(QCStringList::ConstIterator it = qt_list.begin(); it!=qt_list.end(); ++it,c++) PyList_SetItem(l, c, PyBytes_FromString( (*it).data() ) ); return l; } } PyMethodDef PCOPMethods[] = { { (char*)"dcop_call", PythonDCOP::dcop_call, METH_VARARGS, (char*)"Make a call to DCOP." }, { (char*)"app_list", PythonDCOP::application_list, METH_VARARGS, (char*)"Return a list of DCOP registered application." }, { (char*)"obj_list", PythonDCOP::object_list, METH_VARARGS, (char*)"Return a list of objects for a DCOP registered application."}, { (char*)"method_list", PythonDCOP::method_list, METH_VARARGS, (char*)"Return a list of methods for a DCOP object."}, { (char*)"register_as", PythonDCOP::register_as, METH_VARARGS, (char*)"Register the application with DCOP."}, { (char*)"create_dcop_object", PythonDCOP::create_dcop_object, METH_VARARGS, (char*)"Creates a DCOP Object instance."}, { (char*)"process_events", PythonDCOP::process_events, METH_VARARGS, (char*)"Processes QT events."}, { (char*)"set_method_list", PythonDCOP::set_method_list, METH_VARARGS, (char*)"Set the list of methods for a DCOP server object."}, { (char*)"connect_dcop_signal", PythonDCOP::connect_DCOP_Signal, METH_VARARGS, (char*)"Connect a dcop signal."}, { (char*)"disconnect_dcop_signal", PythonDCOP::disconnect_DCOP_Signal, METH_VARARGS, (char*)"Disconnect a dcop signal."}, { NULL, NULL, 0, NULL } /* Sentinel */ }; static struct PyModuleDef pcopmodule = { PyModuleDef_HEAD_INIT, "pcop", NULL, -1, PCOPMethods }; extern "C" { PyMODINIT_FUNC PyInit_pcop(void) { return PyModule_Create(&pcopmodule); } }