You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebindings/dcoppython/shell/pcop.cpp

771 lines
21 KiB

/***************************************************************************
* 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 <tqapplication.h>
#include <tqcstring.h>
#include <tqdatastream.h>
#include <tqfile.h>
#include <tqtextstream.h>
#include <tqstring.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(TTQCString(objid)), m_py_obj(py_obj)
{
m_methods.setAutoDelete(true);
}
PCOPObject::~PCOPObject()
{
}
bool PCOPObject::process(const TTQCString &fun, const TTQByteArray &data,
TTQCString& replyType, TTQByteArray &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 TTQCString &fun, const TTQByteArray &data,
TTQCString& replyType, TTQByteArray &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;
// }
TTQDataStream 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)) {
TTQDataStream 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(TTQAsciiDict<PyObject> meth_list) {
bool ok = true;
for(TTQAsciiDictIterator<PyObject> it(meth_list);
it.current(); ++it) {
PCOPMethod *meth = NULL;
if (ok) {
meth = new PCOPMethod(TTQCString(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;
}
TQCStringList PCOPObject::functions() {
TQCStringList funcs = DCOPObject::functions();
for(TTQAsciiDictIterator<PCOPMethod> it(m_methods);
it.current(); ++it) {
PCOPMethod *meth = it.current();
TTQCString 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(TTQAsciiDictIterator<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 TTQCString &fun) {
return m_methods.find(fun);
}
PCOPType::PCOPType( const TTQCString& 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;
}
TTQCString PCOPType::signature() const
{
TTQCString 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, TTQDataStream& str ) const
{
return Marshaller::instance()->marshal(*this, obj, str);
}
bool PCOPType::isMarshallable( PyObject *obj ) const
{
return Marshaller::instance()->canMarshal(*this, obj);
}
PyObject* PCOPType::demarshal( TTQDataStream& str ) const
{
return Marshaller::instance()->demarshal(*this, str);
}
PCOPMethod::PCOPMethod( const TTQCString& 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
TTQCString 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 += "(";
TQListIterator<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 TQCStringList& methods )
{
m_methods.setAutoDelete( true );
TQCStringList::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 TTQCString &name, PyObject *argTuple )
{
if ( !argTuple )
return m_methods[ name ];
TTQAsciiDictIterator<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 TTQApplication(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;
TTQByteArray replyData;
TTQCString replyType;
TTQByteArray data;
TTQDataStream params( data, IO_WriteOnly );
TTQCString appname( arg1 );
TTQCString objname( arg2 );
TTQCString 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;
TQCStringList funcs = dcop->remoteFunctions( appname, objname, &ok );
if ( !ok )
{
PyErr_SetString( PyExc_RuntimeError, "Object is not accessible." );
return NULL;
}
// for ( TQCStringList::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;
}
TTQCString 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 );
TTQDataStream reply(replyData, IO_ReadOnly);
return type.demarshal( reply );
}
PyObject* application_list( PyObject */*self*/, PyObject */*args*/ )
{
TQCStringList apps = Client::instance()->dcop()->registeredApplications();
PyObject *l = PyList_New( apps.count() );
TQCStringList::ConstIterator it = apps.begin();
TQCStringList::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)) {
TQCStringList objects = Client::instance()->dcop()->remoteObjects(TTQCString(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)) {
TQCStringList methods = Client::instance()->dcop()->remoteFunctions(TTQCString(app), TTQCString(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)) {
TTQCString actual_appid = Client::instance()->dcop()->registerAs(TTQCString(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
TTQAsciiDict<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(TTQCString(sender), TTQCString(senderObj), TTQCString(signal), TTQCString(receiverObj), TTQCString(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(TTQCString(sender), TTQCString(senderObj), TTQCString(signal), TTQCString(receiverObj), TTQCString(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 TQCStringList &qt_list) {
PyObject *l = PyList_New(qt_list.count());
uint c=0;
for(TQCStringList::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 );
}
}