4 from PyQt4.QtXml import QDomDocument
5 from sface.xmlwidget import XmlWindow, DomModel, XmlView, XmlDelegate, ElemNodeDelegate, TextNodeDelegate, CommentNodeDelegate
7 from PyQt4.QtCore import *
8 from PyQt4.QtGui import *
16 def store(self, rawOutput):
17 self.rawOutput = rawOutput
19 def parseMethodCall(self, mc):
25 tree = etree.fromstring(mc)
26 except etree.XMLSyntaxError, e:
29 if tree.tag != "methodCall" or (len(list(tree))==0):
33 (pythonParams, methodName) = xmlrpclib.loads(etree.tostring(tree))
34 except: # in case something went wrong when unmarashaling...
36 methodName = "<exception in parseMethodCall>"
38 request["args"] = [str(x) for x in pythonParams]
39 request["methodName"] = methodName
43 def parseMethodResponse(self, mr):
44 mr = str(mr) # PyQT supplies a QByteArray; make it a string
46 response = {"kind": "unknown"}
49 tree = etree.fromstring(mr)
50 except etree.XMLSyntaxError, e:
51 print "failed to parse XML response", str(e)
52 #file("badparse.xml","w").write(mr)
55 if tree.tag != "methodResponse" or (len(list(tree))==0):
58 # a fault should look like:
61 # faultString: "Register: Missing authority..."
63 faults = tree.xpath("//methodResponse/fault")
65 response["kind"] = "fault"
66 structs = fault.xpath("value/struct")
67 for struct in structs:
68 members = struct.xpath("member")
69 for member in members:
70 names = member.xpath("name")
71 values = member.xpath("value")
72 if (names) and (values):
75 if len(list(value))>0:
77 response[name.text] = data.text
78 # once we have the first fault, return
81 # whatever didn't fault must have succeeded?
82 # "success" really isn't quite the right word. The best we can say is
83 # that the call was executed. The actual op may or may not have
85 response["kind"] = "executed"
90 pttrnAsk = '<methodCall>.*?</methodCall>'
91 pttrnAns = '<methodResponse>.*?</methodResponse>'
92 requests = re.compile(pttrnAsk, re.DOTALL).findall(self.rawOutput)
93 replies = re.compile(pttrnAns, re.DOTALL).findall(self.rawOutput)
95 requests = [ x.replace('\\n','\n') for x in requests ]
96 replies = [ x.replace('\\n','\n').replace("'\nbody: '", '').replace("\"\nbody: '", '') for x in replies ]
97 # A well-formed XML document must have one, and only one, top-level element
102 for requestXml in requests:
103 self.xml += requestXml
104 callDict = {"request": requestXml}
105 # we could have less responses than calls, so guard the pop
107 replyXml = replies.pop(0)
110 # parse the response to extract fault information
111 responseDict = self.parseMethodResponse(replyXml)
112 self.responses.append(responseDict)
114 requestDict = self.parseMethodCall(requestXml)
116 callDict["reply"] = replyXml
117 callDict.update(responseDict)
118 callDict.update(requestDict)
120 self.calls.append(callDict)
122 # just in case somehow we ended up with more responses than calls
124 replyXml = replies.pop()
126 self.responses.append(self.parseMethodResponse(replyXml))
131 # statistics: round-trip time, size of the com
134 class XmlrpcTracker(QWidget):
135 def __init__(self, parent=None):
136 QWidget.__init__(self, parent=parent)
138 self.reader = XmlrpcReader()
140 labelCalls = QLabel("Calls: (from most recent to least recent)")
142 self.callTable = QTableWidget(self)
143 self.callTable.setSelectionBehavior(QAbstractItemView.SelectRows)
144 self.callTable.setSelectionMode(QAbstractItemView.SingleSelection)
145 self.callTable.setMaximumHeight(140)
147 labelArgs = QLabel("Params:")
149 self.argsTable = QTableWidget(self)
150 self.argsTable.setMaximumHeight(140)
152 labelXml = QLabel("XMLRPC Contents")
153 self.xmlView = XmlView(self)
155 self.delegate = XmlDelegate(self)
156 self.delegate.insertNodeDelegate('element', ElemNodeDelegate())
157 self.delegate.insertNodeDelegate('text', TextNodeDelegate())
158 self.delegate.insertNodeDelegate('comment', CommentNodeDelegate())
159 self.xmlView.setItemDelegate(self.delegate)
161 self.layout = QVBoxLayout()
162 self.layout.addWidget(labelCalls)
163 self.layout.addWidget(self.callTable)
164 self.layout.addWidget(labelArgs)
165 self.layout.addWidget(self.argsTable)
166 self.layout.addWidget(labelXml)
167 self.layout.addWidget(self.xmlView)
169 self.setLayout(self.layout)
171 self.connect(self.callTable, SIGNAL("itemSelectionChanged ()"), self.onCallSelect)
173 def getAndPrint(self, rawOutput):
174 self.reader.store(rawOutput)
175 self.reader.extractXml()
176 self.updateCallTable()
178 def updateCallTable(self):
179 self.callTable.clear()
180 self.callTable.setColumnCount(2)
181 self.callTable.setHorizontalHeaderLabels(["name", "status"])
183 calls = self.reader.calls
184 self.callTable.setRowCount(len(calls))
187 reverse_calls = list(enumerate(calls))
188 reverse_calls.reverse()
189 for (index,call) in reverse_calls:
190 item = QTableWidgetItem(call.get("methodName", "unknown"))
191 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
192 item.setData(Qt.UserRole, index)
193 self.callTable.setItem(row, 0, item)
195 item = QTableWidgetItem(call.get("kind", "unknown"))
196 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
197 item.setData(Qt.UserRole, index)
198 self.callTable.setItem(row, 1, item)
202 self.callTable.resizeColumnsToContents()
204 def updateArgsTable(self, args):
205 self.argsTable.clear()
206 self.argsTable.setColumnCount(1)
207 self.argsTable.setHorizontalHeaderLabels(["param"])
208 self.argsTable.horizontalHeader().hide()
209 self.argsTable.setHorizontalScrollMode(QAbstractItemView.ScrollPerPixel)
211 self.argsTable.setRowCount(len(args))
216 arg = arg[:255] + "..."
218 item = QTableWidgetItem(arg)
219 item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
220 self.argsTable.setItem(row, 0, item)
224 self.argsTable.resizeColumnsToContents()
226 def onCallSelect(self):
227 selItems = self.callTable.selectedItems()
228 if (len(selItems) <= 0):
231 row = selItems[0].data(Qt.UserRole).toInt()[0]
233 calls = self.reader.calls
239 xml = "<debug>" + call.get("request","") + call.get("reply", "") + "</debug>"
240 #xml = call.get("request","") + call.get("reply", "")
244 self.updateArgsTable(call.get("args",[]))
246 def displayXml(self, xml):
247 self.document = QDomDocument("XMLRPC Tracker")
248 self.document.setContent(xml)
249 self.model = DomModel(self.document, self)
250 self.xmlView.setModel(self.model)
253 # There's one global tracker for XMLRPCs. Use init_tracker() to create it, and
254 # get_tracker() to get it.
258 def init_tracker(parent):
261 glo_tracker = XmlrpcTracker(parent)