diff options
Diffstat (limited to 'dcoppython/shell/pcop.cpp')
-rw-r--r-- | dcoppython/shell/pcop.cpp | 770 |
1 files changed, 770 insertions, 0 deletions
diff --git a/dcoppython/shell/pcop.cpp b/dcoppython/shell/pcop.cpp new file mode 100644 index 00000000..d7c4adc6 --- /dev/null +++ b/dcoppython/shell/pcop.cpp @@ -0,0 +1,770 @@ +/*************************************************************************** + * 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 <kdebug.h> + +#include <qapplication.h> +#include <qcstring.h> +#include <qdatastream.h> +#include <qfile.h> +#include <qtextstream.h> +#include <qstring.h> + +#include <dcopclient.h> + +#include <assert.h> + +#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(QCString(objid)), m_py_obj(py_obj) + { + m_methods.setAutoDelete(true); + } + + PCOPObject::~PCOPObject() + { + } + + bool PCOPObject::process(const QCString &fun, const QByteArray &data, + QCString& replyType, QByteArray &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 QCString &fun, const QByteArray &data, + QCString& replyType, QByteArray &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; +// } + + QDataStream str_arg(data, IO_ReadOnly); + PyObject *args = PyTuple_New( meth->paramCount() ); + for(int c=0;c<meth->paramCount();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)) { + QDataStream 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(QAsciiDict<PyObject> meth_list) { + bool ok = true; + + for(QAsciiDictIterator<PyObject> it(meth_list); + it.current(); ++it) { + + PCOPMethod *meth = NULL; + if (ok) { + meth = new PCOPMethod(QCString(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(QAsciiDictIterator<PCOPMethod> it(m_methods); + it.current(); ++it) { + PCOPMethod *meth = it.current(); + QCString 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(QAsciiDictIterator<PCOPMethod> it(m_methods); + it.current(); ++it, ++c) { + PyObject *tuple = PyTuple_New(2); + PyList_SetItem(result, c, tuple); + PyTuple_SetItem(tuple, 0, PyString_FromString(it.currentKey() ) ); + PyTuple_SetItem(tuple, 1, it.current()->pythonMethod() ); + } + return result; + } + + PCOPMethod *PCOPObject::matchMethod(const QCString &fun) { + return m_methods.find(fun); + } + + + PCOPType::PCOPType( const QCString& 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; + } + + QCString PCOPType::signature() const + { + QCString 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, QDataStream& str ) const + { + return Marshaller::instance()->marshal(*this, obj, str); + } + + bool PCOPType::isMarshallable( PyObject *obj ) const + { + return Marshaller::instance()->canMarshal(*this, obj); + } + + PyObject* PCOPType::demarshal( QDataStream& str ) const + { + return Marshaller::instance()->demarshal(*this, str); + } + + PCOPMethod::PCOPMethod( const QCString& 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 + QCString 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 += "("; + + QListIterator<PCOPType> 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 QCString &name, PyObject *argTuple ) + { + if ( !argTuple ) + return m_methods[ name ]; + + QAsciiDictIterator<PCOPMethod> 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 QApplication(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; + + QByteArray replyData; + QCString replyType; + QByteArray data; + QDataStream params( data, IO_WriteOnly ); + + QCString appname( arg1 ); + QCString objname( arg2 ); + QCString 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 ) { + // qDebug( "%s", (*it).data() ); + // } + + // + // Create a parse tree and search for the method + // + // ### Check wether that is sane + PCOPClass c( funcs ); + + // qDebug("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; + } + + QCString 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 ); + QDataStream 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, PyString_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(QCString(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(QCString(app), QCString(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)) { + QCString actual_appid = Client::instance()->dcop()->registerAs(QCString(appid), add_pid!=0); + return PyString_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 PyCObject_FromVoidPtr( (void*)obj, delete_dcop_object ); + } + return NULL; + } + + /** + * pcop.set_method_list( <dcopobject cobject>, <method list> ) + * where <method list> 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) && + PyCObject_Check(c_obj) && + PyList_Check(method_list)) { + + // extract each tuple from the list, aborting if any is invalid + QAsciiDict<PyObject> meth_list; + int size = PyList_Size(method_list); + for(int c=0;c<size;c++) { + PyObject *tuple = PyList_GetItem(method_list,c); + const char *method_signature = NULL; + PyObject *py_method = NULL; + if (!PyArg_ParseTuple(tuple, (char*)"sO", &method_signature, &py_method)) + return NULL; + Py_INCREF(py_method); + meth_list.insert(method_signature, py_method); + } + + PCOPObject *obj = (PCOPObject*)PyCObject_AsVoidPtr(c_obj); + if (obj) { + if (!obj->setMethodList(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 (PyCObject_Check(c_obj)) { + PCOPObject *obj = (PCOPObject*)PyCObject_AsVoidPtr(c_obj); + 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(QCString(sender), QCString(senderObj), QCString(signal), QCString(receiverObj), QCString(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(QCString(sender), QCString(senderObj), QCString(signal), QCString(receiverObj), QCString(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, PyString_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 */ +}; + +extern "C" +{ + + void initpcop() + { + (void) Py_InitModule( (char*)"pcop", PCOPMethods ); + } + +} + + |