diff options
Diffstat (limited to 'python/pykde/extensions/dcopexport.py')
-rw-r--r-- | python/pykde/extensions/dcopexport.py | 196 |
1 files changed, 196 insertions, 0 deletions
diff --git a/python/pykde/extensions/dcopexport.py b/python/pykde/extensions/dcopexport.py new file mode 100644 index 00000000..d724adef --- /dev/null +++ b/python/pykde/extensions/dcopexport.py @@ -0,0 +1,196 @@ +""" +Copyright 2004 Jim Bublitz + +Terms and Conditions + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +Except as contained in this notice, the name of the copyright holder shall +not be used in advertising or otherwise to promote the sale, use or other +dealings in this Software without prior written authorization from the +copyright holder. +""" + +""" +This is a re-implementation in Python of pcop.cpp written by Torben Weis +and Julian Rockey, modified for Python and PyKDE. + +To "DCOP-enable" an application, subclass DCOPExObj (be sure to call the +base class' __init__ method) and use 'addMethod' to identify the methods +which will be exposed via DCOP along with names of the Python methods that +implement the exposed methods. + +A DCOP client application when doing DCOPCLient.call (...) will end up +running the 'process' method which demarshalls the arguments, calls the +specified Python method with the arg values passed in, and marshalls the +return value to be returned to the caller. + +DCOPExMeth is basically a data structure to hold the parsed method info +(name, arg list, return type, signature) + +This module requires the dcopext module, but only for the numericTypes and +stringTypes lists +""" + + +from dcop import DCOPObject, DCOPClient +from kdecore import dcop_add, dcop_next +from qt import QString, QCString, QDataStream, IO_ReadOnly, IO_WriteOnly + +numericTypes = ["char", "bool", "short", "int", "long", "uchar", "ushort", "uint", "ulong", + "unsigned char", "unsigned short", "unsigned int", "unsigned long", + "Q_INT32", "pid_t", "float", "double"] +stringTypes = ["QString", "QCString"] + +class DCOPExObj (DCOPObject): + def __init__ (self, objid = None): + if isinstance (objid, str): + DCOPObject.__init__ (self, objid) + else: + DCOPObject.__init__ (self) + + self.methods = {} + + def process (self, meth, data, replyType, replyData): + # normalize the method signature received + meth = str (DCOPClient.normalizeFunctionSignature (meth)).replace (">", "> ") + + # see if this method is available from us via DCOP + # if we don't have it, maybe DCOPObject already provides it (eg - qt object) + if not self.matchMethod (meth): + return DCOPObject.process(self, meth, data, replyType, replyData); + + # demarshall the arg list for the actual method call and call the method + s = QDataStream (data, IO_ReadOnly) + arglist = [] + count = len (self.method.argtypes) + if count == 0: + result = self.method.pymethod () + else: + for i in range (len (self.method.argtypes)): + arglist.append (dcop_next (s, QCString (self.method.argtypes [i]))) + + result = self.method.pymethod (*arglist) + + # marshall the result as 'replyData' + if self.method.rtype != "void": + s = QDataStream (replyData, IO_WriteOnly) + if self.method.rtype in numericTypes: + dcop_add (s, result, self.method.rtype) + elif self.method.rtype in stringTypes and isinstance (result, str): + dcop_add (s, eval ("%s('''%s''')" % (self.method.rtype, result))) + elif self.method.rtype.startswith ("QMap") or self.method.rtype.startswith ("QValueList"): + dcop_add (params, args [i], self.argtypes [i]) + else: + dcop_add (s, result) + + # use append because we want to return the replyType reference, + # not a new QCString + replyType.append (self.method.rtype) + + # success + return True + + def addMethod (self, signature, pymethod): + """ + add a method to the dict - makes it available to DCOP + signature - a string representing the C++ form of the method declaration + with arg names removed (eg + pymethod - the Python method corresponding to the method in signature + + example: + def someMethod (a, b): + return str (a + b) + + signature = "QString someMethod (int, int)" + pymethod = someMethod + self.addMethod (signature, pymethod) + + note that in this case you could add a second entry: + + self.addMethod ("QString someMethod (float, float)", someMethod) + + pymethod can also be a class method, for example - self.someMethod or + someClass.someMethod. In the second case, someClass has to be an instance + of a class (perhaps SomeClass), not the class itself. + + self.methods is a dict holding all of the methods exposed, indexed by + method signature. In the example above, the signature would be: + + someMethod(QString,QString) + + or everything but the return type, which is stored in the dict entry. + The dict entry is a DCOPExMeth instance. + """ + method = DCOPExMeth (signature, pymethod) + if method.sig: + self.methods [method.sig] = method + return method.sig != None + + def matchMethod (self, meth): + # find the method in the dict if it's there + self.method = None + if meth in self.methods: + self.method = self.methods [meth] + return self.method != None + + def functions (self): + # build the list of methods exposed for 'remoteFunctions' calls + # from the entries in the self.methods dict + funcs = DCOPObject.functions (self) + for func in self.methods.keys (): + funcs.append (" ".join ([self.methods [func].rtype, func])) + return funcs; + +class DCOPExMeth: + """ + Encapsulates all of the method data - signature, arg list, return type + and corresponding Python method to be called + """ + def __init__ (self, method, pymethod): + self.pymethod = pymethod + if not self.parseMethod (method): + self.fcnname = self.sig = self.rtype = self.argtypes = None + + def parseMethod (self, method): + # strip whitespace + method = str (DCOPClient.normalizeFunctionSignature (method)).replace (">", "> ") + + # the return type (rtype) and signature (sig) + self.rtype, tail = method.split (" ", 1) + self.sig = tail + if not tail: + return False + self.rtype = self.rtype.strip () + + i = tail.find ("(") + if i < 1: + return False + + # the name of the method + self.fcnname = tail [:i].strip () + "(" + + # the list of arg types + self.argtypes = [] + args = tail [i + 1 : -1].split (",") + if args and args != [""]: + for arg in args: + self.argtypes.append (arg.strip ()) + + return True |