summaryrefslogtreecommitdiffstats
path: root/python/pykde/extensions
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit90825e2392b2d70e43c7a25b8a3752299a933894 (patch)
treee33aa27f02b74604afbfd0ea4f1cfca8833d882a /python/pykde/extensions
downloadtdebindings-90825e2392b2d70e43c7a25b8a3752299a933894.tar.gz
tdebindings-90825e2392b2d70e43c7a25b8a3752299a933894.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebindings@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'python/pykde/extensions')
-rw-r--r--python/pykde/extensions/dcopexport.py196
-rw-r--r--python/pykde/extensions/dcopext.py721
2 files changed, 917 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
diff --git a/python/pykde/extensions/dcopext.py b/python/pykde/extensions/dcopext.py
new file mode 100644
index 00000000..539c4d6c
--- /dev/null
+++ b/python/pykde/extensions/dcopext.py
@@ -0,0 +1,721 @@
+#!/usr/bin/env python
+# -*- coding: ISO-8859-1 -*-
+
+"""
+Copyright 2004 Jim Bublitz (original author)
+ 2006 Mathias Panzenböck (panzi) <grosser.meister.morti@gmx.net>
+
+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.
+"""
+
+import re
+from dcop import DCOPClient
+from qt import QString, QCString, QByteArray, QDataStream, IO_ReadOnly, IO_WriteOnly
+from kdecore import dcop_add, dcop_next
+
+# XXX: 64 bit integers might be handeld wrong! pythons int is AFAIK 32 bit,
+# but pythons long is a arbitrary-precision integer. how to handle that?
+#
+# I think 64 bit types would be:
+# long long, unsigned long long, long long int, unsigned long long int,
+# Q_LLONG, Q_ULLONG, Q_INT64, Q_UINT64
+#
+# and on some (most?) systems:
+# QtOffset
+
+# add complex? complex is c99, not c++
+# but python has a complex type
+POD = set(['char','short','int','long','float','double'])
+typedefIntTypes = set(["uchar", "ushort", "uint", "ulong",
+ "Q_INT8", "Q_INT16", "Q_INT32", "Q_LONG",
+ "Q_UINT8", "Q_UINT16", "Q_UINT32", "Q_ULONG",
+ "sitze_t", "ssize_t", "int8_t", "int16_t", "int32_t",
+ "uint8_t", "uint16_t", "uint32_t", "pid_t", "uid_t",
+ "off_t"])
+# XXX string and std::string too?
+stringTypes = set(["QString", "QCString"])
+pythonStringTypes = set([QString, QCString, str])
+stringTypesDict = {"QString":QString,"QCString":QCString,"str":str,"unicode":unicode}
+
+VOID = 0
+BOOLEAN = 1 # XXX bool is not supported by dcop_add, but maybe some time...
+INTEGER = 2
+FLOAT = 3
+STRING = 4
+CLASS = 5
+
+"""
+(Most of this code is adapted from pydcop in kde-bindings, written by
+Torben Weis and Julian Rockey)
+
+The three classes below (DCOPApp, DCOPObj and DCOPMeth)
+allow transparent Python calls to DCOP methods. For example:
+
+ d = DCOPApp ("kicker", dcop)
+
+(where "kicker" is the complete name of an application and 'dcop' is
+the dcopClient instance owned by the KApplication creating the DCOPApp
+instance) creates a DCOPApp instance. All of the classes in this
+file "borrow" a DCOPClient instance from the calling application.
+
+ d.objects
+
+will return a list of the DCOP objects the application supplies.
+
+ o = d.object ("Panel")
+
+will return a DCOPObj corresponding to applications "Panel" DCOP object.
+
+Similarly:
+
+ o.methods
+
+will return a list of the methods the object supplies and
+
+ m = o.method ("panelSize")
+
+will return a DCOPMeth corresponding to Panel's panelSize() method.
+The m instance also holds the methods return type, list of argument types
+(argtypes) and argument names (argnames).
+
+ m.valid
+
+is a boolean which indicates if the method encapsulated by m is a valid
+method for the application/object specified.
+
+However it isn't necessary to explicitly create the DCOPObj and DCOPMeth.
+
+ d.Panel.panelSize.valid
+
+for example, will also indicate if the method is valid without creating the
+intermediate 'o' and 'm' instances explicitly.
+
+ d = DCOPApp ("kicker", dcop)
+ ok, res = d.Panel.panelSize ()
+
+is all the code necessary to perform the indicated DCOP call and return the
+value the call returns. In this case, panelSize takes no arguments and
+returns an int. 'ok' returns the status of the DCOP call (success = True,
+failure = False).
+
+ ok = d.Panel.addURLButton (QString ("http://www.kde.org"))
+
+would call addURLButton with the required argument, and return nothing but the DCOP call
+status(since its return type is 'void').
+
+Note that to instantiate a DCOPObj directly, you need to have a valid DCOPApp
+to pass to DCOPObj's __init__ method. Similarly, DCOPMeth requires a valid DCOPOBject.
+For example:
+
+ d = DCOPApp ("kicker", dcop)
+ o = DCOPObj (d, "Panel")
+ m = DCOPMeth (o, "panelSize")
+
+or
+
+ m = DCOPMeth (DCOPObj (DCOPApp ("kicker", dcop), "Panel"), "panelSize")
+
+"""
+
+# support stuff:
+def _xiter(*seqences):
+ iters = [iter(seq) for seq in seqences]
+
+ try:
+ while True:
+ yield [it.next() for it in iters]
+
+ except StopIteration:
+ pass
+
+def isStringType(s):
+ for stringType in pythonStringTypes:
+ if isinstance(s,stringType):
+ return True
+ return False
+
+# method syntax:
+# --------------
+# method ::= rtype identifier( args )
+# rtype ::= "void" | type
+# identifier ::= [_a-zA-Z][_a-zA-Z0-9]*
+# args ::= ( arg ("," arg)* )?
+# arg ::= type identifier?
+# type ::= namespace typespec | POD
+# POD ::= ( "unsigned" | "signed" )? identifier
+# namespace ::= (identifier "::")* | "::"
+# typespec ::= identifier ( "<" tpyelist ">" )?
+# typelist ::= (type | int) ("," (type | int) )*
+# int ::= "0x" [0-9a-fA-F]+ | [0-9]+
+
+class MethodParser(object):
+ ident_r = re.compile("[_a-zA-Z][_a-zA-Z0-9]*")
+ num_r = re.compile("0x[0-0a-fA-F]+|[0-9]+")
+
+ def __init__(self,method):
+ self.method = str(method)
+ self.rtype = None
+ self.name = None
+ self.args = []
+
+ self.parseMethod()
+
+ def __repr__(self):
+ return "%s(%s)" % (self.__class__.__name__, repr(self.method))
+
+ def getDecl(self):
+ return ''.join([self.name, '(', ','.join(argtp for (argtp, kind), argname in self.args), ')'])
+
+ def parseMethod(self):
+ i = self.parseRtype(self.method,0)
+ i, self.name = self.parseIdentifier(self.method,i)
+ i = self.parseArgs(self.method,i)
+
+ if i != len(self.method):
+ raise SyntaxError, "invalid function definition: %s" % self.method
+
+ @staticmethod
+ def skipws(s,i):
+ while s[i:i+1].isspace():
+ i += 1
+ return i
+
+ def parseArg(self,s,i):
+ i, tp = self.parseType(s,i)
+ name = self.parseIdentifier(s,i)
+
+ if name:
+ i, name = name
+ else:
+ name = None
+
+ return i, (tp, name)
+
+ def parseIdentifier(self,s,i):
+ i = MethodParser.skipws(s,i)
+ m = MethodParser.ident_r.match(s,i)
+
+ if m:
+ return m.end(), s[i:m.end()]
+ else:
+ return False
+
+ def parseInteger(self,s,i):
+ i = MethodParser.skipws(s,i)
+ m = MethodParser.num_r.match(s,i)
+
+ if m:
+ return m.end(), s[i:m.end()]
+ else:
+ return False
+
+ def parseArgs(self,s,i):
+ i = MethodParser.skipws(s,i)
+
+ if s[i:i+1] == '(':
+ i += 1
+ i = MethodParser.skipws(s,i)
+
+ while i < len(s) and s[i:i+1] != ')':
+ i, arg = self.parseArg(s,i)
+ i = MethodParser.skipws(s,i)
+
+ self.args.append(arg)
+
+ if s[i:i+1] == ',':
+ i += 1
+
+ else:
+ break
+
+ if s[i:i+1] == ')':
+ i += 1
+ else:
+ raise SyntaxError, "missing ')'."
+ else:
+ raise SyntaxError, "missing '('."
+
+ return i
+
+ def parseType(self,s,i):
+ num = self.parseNumberType(s,i)
+
+ if num:
+ return num
+
+ i, ns = self.parseNamespace(s,i)
+ i, tp = self.parseTypespec(s,i)
+
+ tp = ns + tp
+
+ if tp in stringTypes:
+ return i, (tp, STRING)
+
+ else:
+ return i, (tp, CLASS)
+
+ def parseTypespec(self,s,i):
+ i, tp = self.parseIdentifier(s,i)
+ i, tplst = self.parseTypelist(s,i)
+
+ return i, tp + tplst
+
+ def parseTypelist(self,s,i):
+ L = []
+ newi = MethodParser.skipws(s,i)
+
+ if s[newi:newi+1] == '<':
+ i = newi + 1
+ i = MethodParser.skipws(s,i)
+
+ L.append('<')
+
+ while i < len(s) and s[i:i+1] != '>':
+ # template-parameter can be integers!!
+
+ num = self.parseInteger(s,i)
+
+ if num:
+ i, tp = num
+
+ else:
+ i, (tp, kind) = self.parseType(s,i)
+
+ i = MethodParser.skipws(s,i)
+
+ L.append(tp)
+
+ if s[i:i+1] == ',':
+ i += 1
+ L.append(',')
+
+ else:
+ break
+
+
+ if s[i:i+1] == '>':
+ i += 1
+ L.append('>')
+
+ else:
+ raise SyntaxError, "missing '>'."
+
+ return i, ''.join(L)
+
+ def parseNumberType(self,s,i):
+ i, tp = self.parseIdentifier(s,i)
+ L = []
+
+ if tp == 'bool':
+ return i, (tp, BOOLEAN)
+
+ elif tp in typedefIntTypes:
+ return i, (tp, INTEGER)
+
+ elif tp in ('signed','unsigned'):
+ L.append(tp)
+ next = self.parseIdentifier(s,i)
+
+ if next and next[1] in POD:
+ i, tp = next
+
+ else:
+ # type can be fully quallyfied here!
+ return i, (tp, INTEGER)
+
+ if tp in POD:
+ L.append(tp)
+
+ else:
+ # else no number-type at all!
+
+ return False
+
+ # long
+ # long int
+ # long long
+ # long long int
+ # long double
+ # short
+ # short int
+
+ if tp == 'short':
+ # short
+
+ next = self.parseIdentifier(s,i)
+
+ if next and next[1] == 'int':
+ # short int
+
+ i, tp = next
+ L.append(tp)
+
+ elif tp == 'long':
+ # long
+
+ next = self.parseIdentifier(s,i)
+
+ if next:
+ if next[1] in ('int', 'double'):
+ # long int
+ # long double
+
+ i, tp = next
+ L.append(tp)
+
+ elif next[1] == 'long':
+ # long long
+ # XXX: this is 64bit! how should I handle this?
+
+ i, tp = next
+ L.append(tp)
+
+ next = self.parseIdentifier(s,i)
+
+ if next and next[1] == 'int':
+ # long long int
+
+ i, tp = next
+ L.append(tp)
+
+ if tp in ('float', 'double'):
+ return i, (' '.join(L), FLOAT)
+
+ else:
+ return i, (' '.join(L), INTEGER)
+
+ #
+ # ::
+ # foo::
+ # ::foo::
+ # foo::bar::
+ # ::foo::bar::
+ # ...
+ def parseNamespace(self,s,i):
+ L = []
+ i = MethodParser.skipws(s,i)
+
+ if s[i:i+2] == "::":
+ i += 2
+ L.append("::")
+
+ while i < len(s):
+ ns = self.parseIdentifier(s,i)
+
+ if not ns:
+ break
+
+ newi, ns = ns
+ newi = MethodParser.skipws(s,newi)
+
+ if s[newi:newi+2] != "::":
+ break
+
+ i = newi + 2
+
+ L.append( ns )
+ L.append( "::" )
+
+ return i, ''.join(L)
+
+
+ def parseRtype(self,s,i):
+ tp = self.parseIdentifier(s,i)
+
+ if tp and tp[1] == 'void':
+ i, tp = tp
+ self.rtype = (tp,VOID)
+
+ else:
+ i, self.rtype = self.parseType(s,i)
+
+ return i
+
+def DCOPAppsIter(client):
+ for app in client.registeredApplications():
+ yield str(app)
+
+class DCOPApp(object):
+ """
+ An object corresponding to an application with a DCOP interface
+
+ Can return a list of the DCOP objects the application exposes,
+ or create and return an instance of a specific DCOP object.
+ """
+ def __init__ (self, name, client):
+ self.appname = name
+ self.appclient = client
+
+ def __getattr__ (self, item ):
+ if item == "objects":
+ objs, ok = self.appclient.remoteObjects(self.appname)
+
+ if ok:
+ return objs
+ else:
+ return None
+
+ return DCOPObj(self, item)
+
+ def __iter__(self):
+ objs, ok = self.appclient.remoteObjects(self.appname)
+
+ if ok:
+ for obj in objs:
+ yield str(obj)
+
+ # sometimes a object-name is not a valid python identifier.
+ # in that case you can use dcopapp['non-valid::object/name']
+ def __getitem__(self,name):
+ return DCOPObj(self, name)
+
+ def object (self, object):
+ return DCOPObj (self, object)
+
+ def __repr__(self):
+ return '%s(%s,%s)' % (self.__class__.__name__,repr(self.appname),repr(self.appclient))
+
+ def __str__(self):
+ return repr(self)
+
+class DCOPObj(object):
+ """
+ An object corresponding to a specific DCOP object owned by a
+ specific application with a DCOP interface
+
+ Can return a list of the DCOP methods the object exposes,
+ or create and return an instance of a specific DCOP method.
+ """
+
+ def __init__ (self, *args):
+ if isStringType(args[0]):
+ self.appname = args [0]
+ self.objclient = args [1]
+ self.objname = args [2]
+ else:
+ self.appname = args [0].appname
+ self.objname = args [1]
+ self.objclient = args [0].appclient
+
+ self.objmethods = self.getMethods()
+
+ def __repr__( self ):
+ return "%s(%s,%s)" % (self.__class__.__name__,repr(self.appname), repr(self.objname))
+
+ def __str__( self ):
+ return repr(self)
+
+ def __getattr__( self, item ):
+ if item == "methods":
+ return self.objmethods
+
+ return DCOPMeth(self, item)
+
+ def __getitem__(self,name):
+ return DCOPMeth(self, name)
+
+ def getMethods(self):
+ flist, ok = self.objclient.remoteFunctions(self.appname, self.objname)
+
+ if ok:
+ return flist
+ else:
+ return None
+
+ def __iter__(self):
+ flist, ok = self.objclient.remoteFunctions(self.appname, self.objname)
+
+ if ok:
+ for meth in flist:
+ yield str(meth)
+
+ def getMethodNames(self):
+ return [MethodParser(meth).name for meth in self.objmethods]
+
+ def getParsedMethods(self):
+ return [MethodParser(meth) for meth in self.objmethods]
+
+ def method(self, method):
+ return DCOPMeth(self, method)
+
+class DCOPMeth(object):
+ """
+ An object corresponding to a specific DCOP method owned by a
+ specific DCOP object.
+ """
+ def __init__(self, dcopObj, name):
+ self.argtypes = []
+ self.argnames = []
+ self.fcnname = []
+ self.rtype = []
+ self.appname = dcopObj.appname
+ self.objname = dcopObj.objname
+ self.methname = name
+ self.client = dcopObj.objclient
+ self.methods = [str(meth) for meth in dcopObj.objmethods]
+ self.valid = self.findMethod()
+#
+# if not self.valid:
+# self.fcnname = self.rtype = self.argtypes = self.argnames = None
+
+ def __repr__( self ):
+ return "%s(%s,%s,%s)" % (self.__class__.__name__,repr(self.appname),repr(self.objname),repr(self.methname))
+
+ def __str__(self):
+ return repr(self)
+
+ def __call__(self, *args):
+ return self.dcop_call(args)
+
+ def __iter__(self):
+ return iter(self.fcnname)
+
+ def dcop_call(self, args):
+ # method valid?
+ if not self.valid:
+ return False, None
+
+ found = self.getMatchingMethod(args)
+
+ if found is None:
+ return False, None
+
+ meth, argtypes = found
+
+ ok, replyType, replyData = self.client.call(self.appname, self.objname, meth, self.__marshall(args,argtypes))
+
+ if ok:
+ return ok, self.__unmarshall(replyData, replyType)
+ else:
+ return ok, None
+
+ def getMatchingMethod(self,args):
+ count = len(args)
+
+ for funct, argtypes in _xiter(self.fcnname, self.argtypes):
+ if len(argtypes) == count:
+ match = True
+
+ for (wanttp, wantkind), have in _xiter(argtypes,args):
+ if wantkind == BOOLEAN:
+ if not isinstance(have, bool):
+ match = False
+ break
+
+ elif wantkind == INTEGER:
+ if not isinstance(have, int):
+ match = False
+ break
+
+ elif wantkind == FLOAT:
+ if not isinstance(have, float):
+ match = False
+ break
+
+ elif wantkind == STRING:
+ if not isStringType(have):
+ match = False
+ break
+
+ elif wanttp != have.__class__.__name__:
+ match = False
+ break
+
+ if match:
+ return funct, argtypes
+ return None
+
+ def findMethod(self):
+ has = False
+
+ for meth in self.methods:
+ fun = MethodParser(meth)
+
+ if fun.name == self.methname:
+ self.argtypes.append([argtp for argtp, argname in fun.args])
+ self.argnames.append([argname for argtp, argname in fun.args])
+ self.rtype.append(fun.rtype)
+ self.fcnname.append(fun.getDecl())
+
+ has = True
+
+ return has
+
+ def __marshall(self, args, argtypes):
+ data = QByteArray()
+ if argtypes == []:
+ return data
+
+ params = QDataStream (data, IO_WriteOnly)
+
+ for arg, (argtype, argkind) in _xiter(args, argtypes):
+ if argkind == BOOLEAN:
+ # XXX for now, let bools be handelt like int
+ dcop_add(params, int(arg), 'int')
+
+ elif argkind in (INTEGER, FLOAT):
+ dcop_add(params, arg, argtype)
+
+ elif argkind == STRING:
+ # convert it to the right string type:
+ if argtype != arg.__class__.__name__:
+ arg = stringTypesDict[argtype](arg)
+
+ dcop_add(params, arg)
+
+ elif argtype.startswith("QMap") or argtype.startswith("QValueList"):
+ dcop_add(params, arg, argtype)
+
+ # XXX:
+ # Is 'isinstance(arg, eval(argtype))' really good?
+ # What if 'argtype' is located in some modul? Like 'qt.QString'.
+ # Then this will fail (but it should not!).
+ # And the worst thing: the eval() will raise a NameError!
+ #
+ # On the other hand 'arg.__class__.__name__ == argtype' has the
+ # disadvantage that it can't be a derived class!
+ #
+ # Would no check at all be better??
+ #
+ # But I doubt a derived class would be ok anyway. I have to check
+ # this in the DCOP-docu, but I think a derived class would not be
+ # correctly unmarshalled, because a derived class could be marshalled
+ # in a total different way to it's super-class.
+ elif arg.__class__.__name__ == argtype:
+ dcop_add(params, arg)
+
+ else:
+ raise TypeError, "expected type %s, got type %s." % (argtype, arg.__class__.__name__)
+
+ return data
+
+ def __unmarshall(self, data, type_):
+ s = QDataStream(data, IO_ReadOnly)
+
+ if str(type_) in stringTypes:
+ return str(dcop_next(s, type_))
+ else:
+ return dcop_next(s, type_)