| | 327 | Example: |
| | 328 | On the server side, just load the instrospection so your |
| | 329 | server has system.multicall. Then on the client: |
| | 330 | |
| | 331 | from twisted.web.xmlrpc import Proxy |
| | 332 | from twisted.web.xmlrpc import MultiCall |
| | 333 | |
| | 334 | proxy = Proxy('url of your server') |
| | 335 | |
| | 336 | multiRPC = Multicall( proxy ) |
| | 337 | # queue a few calls |
| | 338 | multiRPC.system.listMethods() |
| | 339 | multiRPC.system.methodHelp('system.listMethods') |
| | 340 | multiRPC.system.methodSignature('system.listMethods') |
| | 341 | |
| | 342 | def handleResults(resultsIterator): |
| | 343 | for result in resultsIterator: |
| | 344 | print result |
| | 345 | |
| | 346 | multiRPC().addCallback(handleResults) |
| | 347 | |
| | 348 | @param request: the http C{request} object |
| | 349 | @type request: L{http.Request} |
| | 350 | |
| | 351 | @param procedureList: A list of dictionaries, each representing an |
| | 352 | individual rpc call, containing the C{methodName} and the |
| | 353 | C{params} |
| | 354 | @type procedureList: list |
| | 355 | |
| | 356 | @return: L{defer.DeferredList} of the deferreds for each procedure |
| | 357 | in procedure list |
| | 358 | @rtype: L{defer.DeferredList} |
| | 359 | """ |
| | 360 | def callError(failure): |
| | 361 | """ |
| | 362 | errorback to handle individual call errors |
| | 363 | |
| | 364 | Individual errors in a multicall are returned as |
| | 365 | dictionaries. See U{http://www.xmlrpc.com/discuss/msgReader$1208}. |
| | 366 | |
| | 367 | @param result: C{failure} |
| | 368 | @type result: L{Failure} |
| | 369 | |
| | 370 | @rtype: dict |
| | 371 | @return: a dict with keys C{faultCode} and C{faultString} |
| | 372 | """ |
| | 373 | log.err(failure.value) |
| | 374 | return {'faultCode': self.FAILURE, |
| | 375 | 'faultString': failure.value} |
| | 376 | |
| | 377 | def prepareCallResponse(result): |
| | 378 | """ |
| | 379 | callback to convert a call C{response} to a list |
| | 380 | |
| | 381 | The xmlrpc multicall spec expects a list wrapping |
| | 382 | each call response. |
| | 383 | See U{http://www.xmlrpc.com/discuss/msgReader$1208}. |
| | 384 | |
| | 385 | @param result: C{response} |
| | 386 | @type result: any python type |
| | 387 | |
| | 388 | @rtype: list |
| | 389 | @return: a list with response as element 0 |
| | 390 | """ |
| | 391 | return [result] |
| | 392 | |
| | 393 | def run(procedurePath, params): |
| | 394 | """ |
| | 395 | run an individual procedure from the L{procedureList} and |
| | 396 | returns a C{deferred} |
| | 397 | |
| | 398 | |
| | 399 | @param procedurePath: string naming a procedure |
| | 400 | @type procedurePath: str |
| | 401 | |
| | 402 | @param params: list of arguments to be passed to the procedure |
| | 403 | @type params: list |
| | 404 | |
| | 405 | @return: a C{deferred} object with prepareCallResponse and |
| | 406 | callError attached. |
| | 407 | @rtype: L{defer.Deferred} |
| | 408 | """ |
| | 409 | try: |
| | 410 | procedure = self._xmlrpc_parent.lookupProcedure(procedurePath) |
| | 411 | except Exception, e: |
| | 412 | return defer.fail(str(e)).addErrback(callError) |
| | 413 | else: |
| | 414 | if getattr(procedure, 'withRequest', False): |
| | 415 | call = defer.maybeDeferred(procedure, request, *params) |
| | 416 | else: |
| | 417 | call = defer.maybeDeferred(procedure, *params) |
| | 418 | |
| | 419 | call.addErrback(callError) |
| | 420 | call.addCallback(prepareCallResponse) |
| | 421 | return call |
| | 422 | |
| | 423 | def filterResults(results): |
| | 424 | return map(lambda x: x[1], results) |
| | 425 | |
| | 426 | results = [ |
| | 427 | run(procedure['methodName'], procedure['params']) |
| | 428 | for procedure in procedureList] |
| | 429 | |
| | 430 | return (defer.DeferredList(results) |
| | 431 | .addCallback(filterResults)) |
| | 432 | |
| | 433 | xmlrpc_multicall.signature = [['array', 'array']] |
| | 434 | |
| | 435 | |
| | 436 | class MultiCall(xmlrpclib.MultiCall): |
| | 437 | """ |
| | 438 | server -> a object used to boxcar method calls |
| | 439 | |
| | 440 | server should be a twisted xmlrpc Proxy object. |
| | 441 | |
| | 442 | Methods can be added to the MultiCall using normal |
| | 443 | method call syntax e.g.: |
| | 444 | |
| | 445 | proxy = Proxy('http://advogato.org/XMLRPC') |
| | 446 | |
| | 447 | multicall = MultiCall(proxy) |
| | 448 | multicall.add(2,3) |
| | 449 | multicall.add(5,6) |
| | 450 | |
| | 451 | To execute the multicall, call the MultiCall object |
| | 452 | and attach callbacks, errbacks to the returned |
| | 453 | deferred e.g.: |
| | 454 | |
| | 455 | def printResults(iterator): |
| | 456 | for result in iterator: |
| | 457 | print result |
| | 458 | |
| | 459 | d = multicall() |
| | 460 | d.addCallback(printResults) |
| | 461 | """ |
| | 462 | def __call__(self): |
| | 463 | """ |
| | 464 | execute the multicall |
| | 465 | """ |
| | 466 | marshalled_list = [] |
| | 467 | for name, args in self.__call_list: |
| | 468 | marshalled_list.append({ |
| | 469 | 'methodName': name, |
| | 470 | 'params': args}) |
| | 471 | |
| | 472 | def getIterator(results): |
| | 473 | ''' |
| | 474 | callback to return an xmlrpclib |
| | 475 | MultiCallIterator of the results |
| | 476 | ''' |
| | 477 | return xmlrpclib.MultiCallIterator(results) |
| | 478 | |
| | 479 | return self.__server.callRemote( |
| | 480 | 'system.multicall', marshalled_list |
| | 481 | ).addCallback(getIterator) |
| | 482 | |
| | 483 | |