Coverage for /pythoncovmergedfiles/medio/medio/usr/lib/python3.9/xmlrpc/client.py: 28%

715 statements  

« prev     ^ index     » next       coverage.py v7.3.1, created at 2023-09-25 06:05 +0000

1# 

2# XML-RPC CLIENT LIBRARY 

3# $Id$ 

4# 

5# an XML-RPC client interface for Python. 

6# 

7# the marshalling and response parser code can also be used to 

8# implement XML-RPC servers. 

9# 

10# Notes: 

11# this version is designed to work with Python 2.1 or newer. 

12# 

13# History: 

14# 1999-01-14 fl Created 

15# 1999-01-15 fl Changed dateTime to use localtime 

16# 1999-01-16 fl Added Binary/base64 element, default to RPC2 service 

17# 1999-01-19 fl Fixed array data element (from Skip Montanaro) 

18# 1999-01-21 fl Fixed dateTime constructor, etc. 

19# 1999-02-02 fl Added fault handling, handle empty sequences, etc. 

20# 1999-02-10 fl Fixed problem with empty responses (from Skip Montanaro) 

21# 1999-06-20 fl Speed improvements, pluggable parsers/transports (0.9.8) 

22# 2000-11-28 fl Changed boolean to check the truth value of its argument 

23# 2001-02-24 fl Added encoding/Unicode/SafeTransport patches 

24# 2001-02-26 fl Added compare support to wrappers (0.9.9/1.0b1) 

25# 2001-03-28 fl Make sure response tuple is a singleton 

26# 2001-03-29 fl Don't require empty params element (from Nicholas Riley) 

27# 2001-06-10 fl Folded in _xmlrpclib accelerator support (1.0b2) 

28# 2001-08-20 fl Base xmlrpclib.Error on built-in Exception (from Paul Prescod) 

29# 2001-09-03 fl Allow Transport subclass to override getparser 

30# 2001-09-10 fl Lazy import of urllib, cgi, xmllib (20x import speedup) 

31# 2001-10-01 fl Remove containers from memo cache when done with them 

32# 2001-10-01 fl Use faster escape method (80% dumps speedup) 

33# 2001-10-02 fl More dumps microtuning 

34# 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum) 

35# 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow 

36# 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems) 

37# 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix) 

38# 2002-03-17 fl Avoid buffered read when possible (from James Rucker) 

39# 2002-04-07 fl Added pythondoc comments 

40# 2002-04-16 fl Added __str__ methods to datetime/binary wrappers 

41# 2002-05-15 fl Added error constants (from Andrew Kuchling) 

42# 2002-06-27 fl Merged with Python CVS version 

43# 2002-10-22 fl Added basic authentication (based on code from Phillip Eby) 

44# 2003-01-22 sm Add support for the bool type 

45# 2003-02-27 gvr Remove apply calls 

46# 2003-04-24 sm Use cStringIO if available 

47# 2003-04-25 ak Add support for nil 

48# 2003-06-15 gn Add support for time.struct_time 

49# 2003-07-12 gp Correct marshalling of Faults 

50# 2003-10-31 mvl Add multicall support 

51# 2004-08-20 mvl Bump minimum supported Python version to 2.1 

52# 2014-12-02 ch/doko Add workaround for gzip bomb vulnerability 

53# 

54# Copyright (c) 1999-2002 by Secret Labs AB. 

55# Copyright (c) 1999-2002 by Fredrik Lundh. 

56# 

57# info@pythonware.com 

58# http://www.pythonware.com 

59# 

60# -------------------------------------------------------------------- 

61# The XML-RPC client interface is 

62# 

63# Copyright (c) 1999-2002 by Secret Labs AB 

64# Copyright (c) 1999-2002 by Fredrik Lundh 

65# 

66# By obtaining, using, and/or copying this software and/or its 

67# associated documentation, you agree that you have read, understood, 

68# and will comply with the following terms and conditions: 

69# 

70# Permission to use, copy, modify, and distribute this software and 

71# its associated documentation for any purpose and without fee is 

72# hereby granted, provided that the above copyright notice appears in 

73# all copies, and that both that copyright notice and this permission 

74# notice appear in supporting documentation, and that the name of 

75# Secret Labs AB or the author not be used in advertising or publicity 

76# pertaining to distribution of the software without specific, written 

77# prior permission. 

78# 

79# SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD 

80# TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- 

81# ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR 

82# BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY 

83# DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 

84# WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS 

85# ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE 

86# OF THIS SOFTWARE. 

87# -------------------------------------------------------------------- 

88 

89""" 

90An XML-RPC client interface for Python. 

91 

92The marshalling and response parser code can also be used to 

93implement XML-RPC servers. 

94 

95Exported exceptions: 

96 

97 Error Base class for client errors 

98 ProtocolError Indicates an HTTP protocol error 

99 ResponseError Indicates a broken response package 

100 Fault Indicates an XML-RPC fault package 

101 

102Exported classes: 

103 

104 ServerProxy Represents a logical connection to an XML-RPC server 

105 

106 MultiCall Executor of boxcared xmlrpc requests 

107 DateTime dateTime wrapper for an ISO 8601 string or time tuple or 

108 localtime integer value to generate a "dateTime.iso8601" 

109 XML-RPC value 

110 Binary binary data wrapper 

111 

112 Marshaller Generate an XML-RPC params chunk from a Python data structure 

113 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message 

114 Transport Handles an HTTP transaction to an XML-RPC server 

115 SafeTransport Handles an HTTPS transaction to an XML-RPC server 

116 

117Exported constants: 

118 

119 (none) 

120 

121Exported functions: 

122 

123 getparser Create instance of the fastest available parser & attach 

124 to an unmarshalling object 

125 dumps Convert an argument tuple or a Fault instance to an XML-RPC 

126 request (or response, if the methodresponse option is used). 

127 loads Convert an XML-RPC packet to unmarshalled data plus a method 

128 name (None if not present). 

129""" 

130 

131import base64 

132import sys 

133import time 

134from datetime import datetime 

135from decimal import Decimal 

136import http.client 

137import urllib.parse 

138from xml.parsers import expat 

139import errno 

140from io import BytesIO 

141try: 

142 import gzip 

143except ImportError: 

144 gzip = None #python can be built without zlib/gzip support 

145 

146# -------------------------------------------------------------------- 

147# Internal stuff 

148 

149def escape(s): 

150 s = s.replace("&", "&") 

151 s = s.replace("<", "&lt;") 

152 return s.replace(">", "&gt;",) 

153 

154# used in User-Agent header sent 

155__version__ = '%d.%d' % sys.version_info[:2] 

156 

157# xmlrpc integer limits 

158MAXINT = 2**31-1 

159MININT = -2**31 

160 

161# -------------------------------------------------------------------- 

162# Error constants (from Dan Libby's specification at 

163# http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php) 

164 

165# Ranges of errors 

166PARSE_ERROR = -32700 

167SERVER_ERROR = -32600 

168APPLICATION_ERROR = -32500 

169SYSTEM_ERROR = -32400 

170TRANSPORT_ERROR = -32300 

171 

172# Specific errors 

173NOT_WELLFORMED_ERROR = -32700 

174UNSUPPORTED_ENCODING = -32701 

175INVALID_ENCODING_CHAR = -32702 

176INVALID_XMLRPC = -32600 

177METHOD_NOT_FOUND = -32601 

178INVALID_METHOD_PARAMS = -32602 

179INTERNAL_ERROR = -32603 

180 

181# -------------------------------------------------------------------- 

182# Exceptions 

183 

184## 

185# Base class for all kinds of client-side errors. 

186 

187class Error(Exception): 

188 """Base class for client errors.""" 

189 __str__ = object.__str__ 

190 

191## 

192# Indicates an HTTP-level protocol error. This is raised by the HTTP 

193# transport layer, if the server returns an error code other than 200 

194# (OK). 

195# 

196# @param url The target URL. 

197# @param errcode The HTTP error code. 

198# @param errmsg The HTTP error message. 

199# @param headers The HTTP header dictionary. 

200 

201class ProtocolError(Error): 

202 """Indicates an HTTP protocol error.""" 

203 def __init__(self, url, errcode, errmsg, headers): 

204 Error.__init__(self) 

205 self.url = url 

206 self.errcode = errcode 

207 self.errmsg = errmsg 

208 self.headers = headers 

209 def __repr__(self): 

210 return ( 

211 "<%s for %s: %s %s>" % 

212 (self.__class__.__name__, self.url, self.errcode, self.errmsg) 

213 ) 

214 

215## 

216# Indicates a broken XML-RPC response package. This exception is 

217# raised by the unmarshalling layer, if the XML-RPC response is 

218# malformed. 

219 

220class ResponseError(Error): 

221 """Indicates a broken response package.""" 

222 pass 

223 

224## 

225# Indicates an XML-RPC fault response package. This exception is 

226# raised by the unmarshalling layer, if the XML-RPC response contains 

227# a fault string. This exception can also be used as a class, to 

228# generate a fault XML-RPC message. 

229# 

230# @param faultCode The XML-RPC fault code. 

231# @param faultString The XML-RPC fault string. 

232 

233class Fault(Error): 

234 """Indicates an XML-RPC fault package.""" 

235 def __init__(self, faultCode, faultString, **extra): 

236 Error.__init__(self) 

237 self.faultCode = faultCode 

238 self.faultString = faultString 

239 def __repr__(self): 

240 return "<%s %s: %r>" % (self.__class__.__name__, 

241 self.faultCode, self.faultString) 

242 

243# -------------------------------------------------------------------- 

244# Special values 

245 

246## 

247# Backwards compatibility 

248 

249boolean = Boolean = bool 

250 

251## 

252# Wrapper for XML-RPC DateTime values. This converts a time value to 

253# the format used by XML-RPC. 

254# <p> 

255# The value can be given as a datetime object, as a string in the 

256# format "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by 

257# time.localtime()), or an integer value (as returned by time.time()). 

258# The wrapper uses time.localtime() to convert an integer to a time 

259# tuple. 

260# 

261# @param value The time, given as a datetime object, an ISO 8601 string, 

262# a time tuple, or an integer time value. 

263 

264 

265# Issue #13305: different format codes across platforms 

266_day0 = datetime(1, 1, 1) 

267if _day0.strftime('%Y') == '0001': # Mac OS X 

268 def _iso8601_format(value): 

269 return value.strftime("%Y%m%dT%H:%M:%S") 

270elif _day0.strftime('%4Y') == '0001': # Linux 

271 def _iso8601_format(value): 

272 return value.strftime("%4Y%m%dT%H:%M:%S") 

273else: 

274 def _iso8601_format(value): 

275 return value.strftime("%Y%m%dT%H:%M:%S").zfill(17) 

276del _day0 

277 

278 

279def _strftime(value): 

280 if isinstance(value, datetime): 

281 return _iso8601_format(value) 

282 

283 if not isinstance(value, (tuple, time.struct_time)): 

284 if value == 0: 

285 value = time.time() 

286 value = time.localtime(value) 

287 

288 return "%04d%02d%02dT%02d:%02d:%02d" % value[:6] 

289 

290class DateTime: 

291 """DateTime wrapper for an ISO 8601 string or time tuple or 

292 localtime integer value to generate 'dateTime.iso8601' XML-RPC 

293 value. 

294 """ 

295 

296 def __init__(self, value=0): 

297 if isinstance(value, str): 

298 self.value = value 

299 else: 

300 self.value = _strftime(value) 

301 

302 def make_comparable(self, other): 

303 if isinstance(other, DateTime): 

304 s = self.value 

305 o = other.value 

306 elif isinstance(other, datetime): 

307 s = self.value 

308 o = _iso8601_format(other) 

309 elif isinstance(other, str): 

310 s = self.value 

311 o = other 

312 elif hasattr(other, "timetuple"): 

313 s = self.timetuple() 

314 o = other.timetuple() 

315 else: 

316 s = self 

317 o = NotImplemented 

318 return s, o 

319 

320 def __lt__(self, other): 

321 s, o = self.make_comparable(other) 

322 if o is NotImplemented: 

323 return NotImplemented 

324 return s < o 

325 

326 def __le__(self, other): 

327 s, o = self.make_comparable(other) 

328 if o is NotImplemented: 

329 return NotImplemented 

330 return s <= o 

331 

332 def __gt__(self, other): 

333 s, o = self.make_comparable(other) 

334 if o is NotImplemented: 

335 return NotImplemented 

336 return s > o 

337 

338 def __ge__(self, other): 

339 s, o = self.make_comparable(other) 

340 if o is NotImplemented: 

341 return NotImplemented 

342 return s >= o 

343 

344 def __eq__(self, other): 

345 s, o = self.make_comparable(other) 

346 if o is NotImplemented: 

347 return NotImplemented 

348 return s == o 

349 

350 def timetuple(self): 

351 return time.strptime(self.value, "%Y%m%dT%H:%M:%S") 

352 

353 ## 

354 # Get date/time value. 

355 # 

356 # @return Date/time value, as an ISO 8601 string. 

357 

358 def __str__(self): 

359 return self.value 

360 

361 def __repr__(self): 

362 return "<%s %r at %#x>" % (self.__class__.__name__, self.value, id(self)) 

363 

364 def decode(self, data): 

365 self.value = str(data).strip() 

366 

367 def encode(self, out): 

368 out.write("<value><dateTime.iso8601>") 

369 out.write(self.value) 

370 out.write("</dateTime.iso8601></value>\n") 

371 

372def _datetime(data): 

373 # decode xml element contents into a DateTime structure. 

374 value = DateTime() 

375 value.decode(data) 

376 return value 

377 

378def _datetime_type(data): 

379 return datetime.strptime(data, "%Y%m%dT%H:%M:%S") 

380 

381## 

382# Wrapper for binary data. This can be used to transport any kind 

383# of binary data over XML-RPC, using BASE64 encoding. 

384# 

385# @param data An 8-bit string containing arbitrary data. 

386 

387class Binary: 

388 """Wrapper for binary data.""" 

389 

390 def __init__(self, data=None): 

391 if data is None: 

392 data = b"" 

393 else: 

394 if not isinstance(data, (bytes, bytearray)): 

395 raise TypeError("expected bytes or bytearray, not %s" % 

396 data.__class__.__name__) 

397 data = bytes(data) # Make a copy of the bytes! 

398 self.data = data 

399 

400 ## 

401 # Get buffer contents. 

402 # 

403 # @return Buffer contents, as an 8-bit string. 

404 

405 def __str__(self): 

406 return str(self.data, "latin-1") # XXX encoding?! 

407 

408 def __eq__(self, other): 

409 if isinstance(other, Binary): 

410 other = other.data 

411 return self.data == other 

412 

413 def decode(self, data): 

414 self.data = base64.decodebytes(data) 

415 

416 def encode(self, out): 

417 out.write("<value><base64>\n") 

418 encoded = base64.encodebytes(self.data) 

419 out.write(encoded.decode('ascii')) 

420 out.write("</base64></value>\n") 

421 

422def _binary(data): 

423 # decode xml element contents into a Binary structure 

424 value = Binary() 

425 value.decode(data) 

426 return value 

427 

428WRAPPERS = (DateTime, Binary) 

429 

430# -------------------------------------------------------------------- 

431# XML parsers 

432 

433class ExpatParser: 

434 # fast expat parser for Python 2.0 and later. 

435 def __init__(self, target): 

436 self._parser = parser = expat.ParserCreate(None, None) 

437 self._target = target 

438 parser.StartElementHandler = target.start 

439 parser.EndElementHandler = target.end 

440 parser.CharacterDataHandler = target.data 

441 encoding = None 

442 target.xml(encoding, None) 

443 

444 def feed(self, data): 

445 self._parser.Parse(data, False) 

446 

447 def close(self): 

448 try: 

449 parser = self._parser 

450 except AttributeError: 

451 pass 

452 else: 

453 del self._target, self._parser # get rid of circular references 

454 parser.Parse(b"", True) # end of data 

455 

456# -------------------------------------------------------------------- 

457# XML-RPC marshalling and unmarshalling code 

458 

459## 

460# XML-RPC marshaller. 

461# 

462# @param encoding Default encoding for 8-bit strings. The default 

463# value is None (interpreted as UTF-8). 

464# @see dumps 

465 

466class Marshaller: 

467 """Generate an XML-RPC params chunk from a Python data structure. 

468 

469 Create a Marshaller instance for each set of parameters, and use 

470 the "dumps" method to convert your data (represented as a tuple) 

471 to an XML-RPC params chunk. To write a fault response, pass a 

472 Fault instance instead. You may prefer to use the "dumps" module 

473 function for this purpose. 

474 """ 

475 

476 # by the way, if you don't understand what's going on in here, 

477 # that's perfectly ok. 

478 

479 def __init__(self, encoding=None, allow_none=False): 

480 self.memo = {} 

481 self.data = None 

482 self.encoding = encoding 

483 self.allow_none = allow_none 

484 

485 dispatch = {} 

486 

487 def dumps(self, values): 

488 out = [] 

489 write = out.append 

490 dump = self.__dump 

491 if isinstance(values, Fault): 

492 # fault instance 

493 write("<fault>\n") 

494 dump({'faultCode': values.faultCode, 

495 'faultString': values.faultString}, 

496 write) 

497 write("</fault>\n") 

498 else: 

499 # parameter block 

500 # FIXME: the xml-rpc specification allows us to leave out 

501 # the entire <params> block if there are no parameters. 

502 # however, changing this may break older code (including 

503 # old versions of xmlrpclib.py), so this is better left as 

504 # is for now. See @XMLRPC3 for more information. /F 

505 write("<params>\n") 

506 for v in values: 

507 write("<param>\n") 

508 dump(v, write) 

509 write("</param>\n") 

510 write("</params>\n") 

511 result = "".join(out) 

512 return result 

513 

514 def __dump(self, value, write): 

515 try: 

516 f = self.dispatch[type(value)] 

517 except KeyError: 

518 # check if this object can be marshalled as a structure 

519 if not hasattr(value, '__dict__'): 

520 raise TypeError("cannot marshal %s objects" % type(value)) 

521 # check if this class is a sub-class of a basic type, 

522 # because we don't know how to marshal these types 

523 # (e.g. a string sub-class) 

524 for type_ in type(value).__mro__: 

525 if type_ in self.dispatch.keys(): 

526 raise TypeError("cannot marshal %s objects" % type(value)) 

527 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix 

528 # for the p3yk merge, this should probably be fixed more neatly. 

529 f = self.dispatch["_arbitrary_instance"] 

530 f(self, value, write) 

531 

532 def dump_nil (self, value, write): 

533 if not self.allow_none: 

534 raise TypeError("cannot marshal None unless allow_none is enabled") 

535 write("<value><nil/></value>") 

536 dispatch[type(None)] = dump_nil 

537 

538 def dump_bool(self, value, write): 

539 write("<value><boolean>") 

540 write(value and "1" or "0") 

541 write("</boolean></value>\n") 

542 dispatch[bool] = dump_bool 

543 

544 def dump_long(self, value, write): 

545 if value > MAXINT or value < MININT: 

546 raise OverflowError("int exceeds XML-RPC limits") 

547 write("<value><int>") 

548 write(str(int(value))) 

549 write("</int></value>\n") 

550 dispatch[int] = dump_long 

551 

552 # backward compatible 

553 dump_int = dump_long 

554 

555 def dump_double(self, value, write): 

556 write("<value><double>") 

557 write(repr(value)) 

558 write("</double></value>\n") 

559 dispatch[float] = dump_double 

560 

561 def dump_unicode(self, value, write, escape=escape): 

562 write("<value><string>") 

563 write(escape(value)) 

564 write("</string></value>\n") 

565 dispatch[str] = dump_unicode 

566 

567 def dump_bytes(self, value, write): 

568 write("<value><base64>\n") 

569 encoded = base64.encodebytes(value) 

570 write(encoded.decode('ascii')) 

571 write("</base64></value>\n") 

572 dispatch[bytes] = dump_bytes 

573 dispatch[bytearray] = dump_bytes 

574 

575 def dump_array(self, value, write): 

576 i = id(value) 

577 if i in self.memo: 

578 raise TypeError("cannot marshal recursive sequences") 

579 self.memo[i] = None 

580 dump = self.__dump 

581 write("<value><array><data>\n") 

582 for v in value: 

583 dump(v, write) 

584 write("</data></array></value>\n") 

585 del self.memo[i] 

586 dispatch[tuple] = dump_array 

587 dispatch[list] = dump_array 

588 

589 def dump_struct(self, value, write, escape=escape): 

590 i = id(value) 

591 if i in self.memo: 

592 raise TypeError("cannot marshal recursive dictionaries") 

593 self.memo[i] = None 

594 dump = self.__dump 

595 write("<value><struct>\n") 

596 for k, v in value.items(): 

597 write("<member>\n") 

598 if not isinstance(k, str): 

599 raise TypeError("dictionary key must be string") 

600 write("<name>%s</name>\n" % escape(k)) 

601 dump(v, write) 

602 write("</member>\n") 

603 write("</struct></value>\n") 

604 del self.memo[i] 

605 dispatch[dict] = dump_struct 

606 

607 def dump_datetime(self, value, write): 

608 write("<value><dateTime.iso8601>") 

609 write(_strftime(value)) 

610 write("</dateTime.iso8601></value>\n") 

611 dispatch[datetime] = dump_datetime 

612 

613 def dump_instance(self, value, write): 

614 # check for special wrappers 

615 if value.__class__ in WRAPPERS: 

616 self.write = write 

617 value.encode(self) 

618 del self.write 

619 else: 

620 # store instance attributes as a struct (really?) 

621 self.dump_struct(value.__dict__, write) 

622 dispatch[DateTime] = dump_instance 

623 dispatch[Binary] = dump_instance 

624 # XXX(twouters): using "_arbitrary_instance" as key as a quick-fix 

625 # for the p3yk merge, this should probably be fixed more neatly. 

626 dispatch["_arbitrary_instance"] = dump_instance 

627 

628## 

629# XML-RPC unmarshaller. 

630# 

631# @see loads 

632 

633class Unmarshaller: 

634 """Unmarshal an XML-RPC response, based on incoming XML event 

635 messages (start, data, end). Call close() to get the resulting 

636 data structure. 

637 

638 Note that this reader is fairly tolerant, and gladly accepts bogus 

639 XML-RPC data without complaining (but not bogus XML). 

640 """ 

641 

642 # and again, if you don't understand what's going on in here, 

643 # that's perfectly ok. 

644 

645 def __init__(self, use_datetime=False, use_builtin_types=False): 

646 self._type = None 

647 self._stack = [] 

648 self._marks = [] 

649 self._data = [] 

650 self._value = False 

651 self._methodname = None 

652 self._encoding = "utf-8" 

653 self.append = self._stack.append 

654 self._use_datetime = use_builtin_types or use_datetime 

655 self._use_bytes = use_builtin_types 

656 

657 def close(self): 

658 # return response tuple and target method 

659 if self._type is None or self._marks: 

660 raise ResponseError() 

661 if self._type == "fault": 

662 raise Fault(**self._stack[0]) 

663 return tuple(self._stack) 

664 

665 def getmethodname(self): 

666 return self._methodname 

667 

668 # 

669 # event handlers 

670 

671 def xml(self, encoding, standalone): 

672 self._encoding = encoding 

673 # FIXME: assert standalone == 1 ??? 

674 

675 def start(self, tag, attrs): 

676 # prepare to handle this element 

677 if ':' in tag: 

678 tag = tag.split(':')[-1] 

679 if tag == "array" or tag == "struct": 

680 self._marks.append(len(self._stack)) 

681 self._data = [] 

682 if self._value and tag not in self.dispatch: 

683 raise ResponseError("unknown tag %r" % tag) 

684 self._value = (tag == "value") 

685 

686 def data(self, text): 

687 self._data.append(text) 

688 

689 def end(self, tag): 

690 # call the appropriate end tag handler 

691 try: 

692 f = self.dispatch[tag] 

693 except KeyError: 

694 if ':' not in tag: 

695 return # unknown tag ? 

696 try: 

697 f = self.dispatch[tag.split(':')[-1]] 

698 except KeyError: 

699 return # unknown tag ? 

700 return f(self, "".join(self._data)) 

701 

702 # 

703 # accelerator support 

704 

705 def end_dispatch(self, tag, data): 

706 # dispatch data 

707 try: 

708 f = self.dispatch[tag] 

709 except KeyError: 

710 if ':' not in tag: 

711 return # unknown tag ? 

712 try: 

713 f = self.dispatch[tag.split(':')[-1]] 

714 except KeyError: 

715 return # unknown tag ? 

716 return f(self, data) 

717 

718 # 

719 # element decoders 

720 

721 dispatch = {} 

722 

723 def end_nil (self, data): 

724 self.append(None) 

725 self._value = 0 

726 dispatch["nil"] = end_nil 

727 

728 def end_boolean(self, data): 

729 if data == "0": 

730 self.append(False) 

731 elif data == "1": 

732 self.append(True) 

733 else: 

734 raise TypeError("bad boolean value") 

735 self._value = 0 

736 dispatch["boolean"] = end_boolean 

737 

738 def end_int(self, data): 

739 self.append(int(data)) 

740 self._value = 0 

741 dispatch["i1"] = end_int 

742 dispatch["i2"] = end_int 

743 dispatch["i4"] = end_int 

744 dispatch["i8"] = end_int 

745 dispatch["int"] = end_int 

746 dispatch["biginteger"] = end_int 

747 

748 def end_double(self, data): 

749 self.append(float(data)) 

750 self._value = 0 

751 dispatch["double"] = end_double 

752 dispatch["float"] = end_double 

753 

754 def end_bigdecimal(self, data): 

755 self.append(Decimal(data)) 

756 self._value = 0 

757 dispatch["bigdecimal"] = end_bigdecimal 

758 

759 def end_string(self, data): 

760 if self._encoding: 

761 data = data.decode(self._encoding) 

762 self.append(data) 

763 self._value = 0 

764 dispatch["string"] = end_string 

765 dispatch["name"] = end_string # struct keys are always strings 

766 

767 def end_array(self, data): 

768 mark = self._marks.pop() 

769 # map arrays to Python lists 

770 self._stack[mark:] = [self._stack[mark:]] 

771 self._value = 0 

772 dispatch["array"] = end_array 

773 

774 def end_struct(self, data): 

775 mark = self._marks.pop() 

776 # map structs to Python dictionaries 

777 dict = {} 

778 items = self._stack[mark:] 

779 for i in range(0, len(items), 2): 

780 dict[items[i]] = items[i+1] 

781 self._stack[mark:] = [dict] 

782 self._value = 0 

783 dispatch["struct"] = end_struct 

784 

785 def end_base64(self, data): 

786 value = Binary() 

787 value.decode(data.encode("ascii")) 

788 if self._use_bytes: 

789 value = value.data 

790 self.append(value) 

791 self._value = 0 

792 dispatch["base64"] = end_base64 

793 

794 def end_dateTime(self, data): 

795 value = DateTime() 

796 value.decode(data) 

797 if self._use_datetime: 

798 value = _datetime_type(data) 

799 self.append(value) 

800 dispatch["dateTime.iso8601"] = end_dateTime 

801 

802 def end_value(self, data): 

803 # if we stumble upon a value element with no internal 

804 # elements, treat it as a string element 

805 if self._value: 

806 self.end_string(data) 

807 dispatch["value"] = end_value 

808 

809 def end_params(self, data): 

810 self._type = "params" 

811 dispatch["params"] = end_params 

812 

813 def end_fault(self, data): 

814 self._type = "fault" 

815 dispatch["fault"] = end_fault 

816 

817 def end_methodName(self, data): 

818 if self._encoding: 

819 data = data.decode(self._encoding) 

820 self._methodname = data 

821 self._type = "methodName" # no params 

822 dispatch["methodName"] = end_methodName 

823 

824## Multicall support 

825# 

826 

827class _MultiCallMethod: 

828 # some lesser magic to store calls made to a MultiCall object 

829 # for batch execution 

830 def __init__(self, call_list, name): 

831 self.__call_list = call_list 

832 self.__name = name 

833 def __getattr__(self, name): 

834 return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name)) 

835 def __call__(self, *args): 

836 self.__call_list.append((self.__name, args)) 

837 

838class MultiCallIterator: 

839 """Iterates over the results of a multicall. Exceptions are 

840 raised in response to xmlrpc faults.""" 

841 

842 def __init__(self, results): 

843 self.results = results 

844 

845 def __getitem__(self, i): 

846 item = self.results[i] 

847 if type(item) == type({}): 

848 raise Fault(item['faultCode'], item['faultString']) 

849 elif type(item) == type([]): 

850 return item[0] 

851 else: 

852 raise ValueError("unexpected type in multicall result") 

853 

854class MultiCall: 

855 """server -> an object used to boxcar method calls 

856 

857 server should be a ServerProxy object. 

858 

859 Methods can be added to the MultiCall using normal 

860 method call syntax e.g.: 

861 

862 multicall = MultiCall(server_proxy) 

863 multicall.add(2,3) 

864 multicall.get_address("Guido") 

865 

866 To execute the multicall, call the MultiCall object e.g.: 

867 

868 add_result, address = multicall() 

869 """ 

870 

871 def __init__(self, server): 

872 self.__server = server 

873 self.__call_list = [] 

874 

875 def __repr__(self): 

876 return "<%s at %#x>" % (self.__class__.__name__, id(self)) 

877 

878 def __getattr__(self, name): 

879 return _MultiCallMethod(self.__call_list, name) 

880 

881 def __call__(self): 

882 marshalled_list = [] 

883 for name, args in self.__call_list: 

884 marshalled_list.append({'methodName' : name, 'params' : args}) 

885 

886 return MultiCallIterator(self.__server.system.multicall(marshalled_list)) 

887 

888# -------------------------------------------------------------------- 

889# convenience functions 

890 

891FastMarshaller = FastParser = FastUnmarshaller = None 

892 

893## 

894# Create a parser object, and connect it to an unmarshalling instance. 

895# This function picks the fastest available XML parser. 

896# 

897# return A (parser, unmarshaller) tuple. 

898 

899def getparser(use_datetime=False, use_builtin_types=False): 

900 """getparser() -> parser, unmarshaller 

901 

902 Create an instance of the fastest available parser, and attach it 

903 to an unmarshalling object. Return both objects. 

904 """ 

905 if FastParser and FastUnmarshaller: 

906 if use_builtin_types: 

907 mkdatetime = _datetime_type 

908 mkbytes = base64.decodebytes 

909 elif use_datetime: 

910 mkdatetime = _datetime_type 

911 mkbytes = _binary 

912 else: 

913 mkdatetime = _datetime 

914 mkbytes = _binary 

915 target = FastUnmarshaller(True, False, mkbytes, mkdatetime, Fault) 

916 parser = FastParser(target) 

917 else: 

918 target = Unmarshaller(use_datetime=use_datetime, use_builtin_types=use_builtin_types) 

919 if FastParser: 

920 parser = FastParser(target) 

921 else: 

922 parser = ExpatParser(target) 

923 return parser, target 

924 

925## 

926# Convert a Python tuple or a Fault instance to an XML-RPC packet. 

927# 

928# @def dumps(params, **options) 

929# @param params A tuple or Fault instance. 

930# @keyparam methodname If given, create a methodCall request for 

931# this method name. 

932# @keyparam methodresponse If given, create a methodResponse packet. 

933# If used with a tuple, the tuple must be a singleton (that is, 

934# it must contain exactly one element). 

935# @keyparam encoding The packet encoding. 

936# @return A string containing marshalled data. 

937 

938def dumps(params, methodname=None, methodresponse=None, encoding=None, 

939 allow_none=False): 

940 """data [,options] -> marshalled data 

941 

942 Convert an argument tuple or a Fault instance to an XML-RPC 

943 request (or response, if the methodresponse option is used). 

944 

945 In addition to the data object, the following options can be given 

946 as keyword arguments: 

947 

948 methodname: the method name for a methodCall packet 

949 

950 methodresponse: true to create a methodResponse packet. 

951 If this option is used with a tuple, the tuple must be 

952 a singleton (i.e. it can contain only one element). 

953 

954 encoding: the packet encoding (default is UTF-8) 

955 

956 All byte strings in the data structure are assumed to use the 

957 packet encoding. Unicode strings are automatically converted, 

958 where necessary. 

959 """ 

960 

961 assert isinstance(params, (tuple, Fault)), "argument must be tuple or Fault instance" 

962 if isinstance(params, Fault): 

963 methodresponse = 1 

964 elif methodresponse and isinstance(params, tuple): 

965 assert len(params) == 1, "response tuple must be a singleton" 

966 

967 if not encoding: 

968 encoding = "utf-8" 

969 

970 if FastMarshaller: 

971 m = FastMarshaller(encoding) 

972 else: 

973 m = Marshaller(encoding, allow_none) 

974 

975 data = m.dumps(params) 

976 

977 if encoding != "utf-8": 

978 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding) 

979 else: 

980 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default 

981 

982 # standard XML-RPC wrappings 

983 if methodname: 

984 # a method call 

985 data = ( 

986 xmlheader, 

987 "<methodCall>\n" 

988 "<methodName>", methodname, "</methodName>\n", 

989 data, 

990 "</methodCall>\n" 

991 ) 

992 elif methodresponse: 

993 # a method response, or a fault structure 

994 data = ( 

995 xmlheader, 

996 "<methodResponse>\n", 

997 data, 

998 "</methodResponse>\n" 

999 ) 

1000 else: 

1001 return data # return as is 

1002 return "".join(data) 

1003 

1004## 

1005# Convert an XML-RPC packet to a Python object. If the XML-RPC packet 

1006# represents a fault condition, this function raises a Fault exception. 

1007# 

1008# @param data An XML-RPC packet, given as an 8-bit string. 

1009# @return A tuple containing the unpacked data, and the method name 

1010# (None if not present). 

1011# @see Fault 

1012 

1013def loads(data, use_datetime=False, use_builtin_types=False): 

1014 """data -> unmarshalled data, method name 

1015 

1016 Convert an XML-RPC packet to unmarshalled data plus a method 

1017 name (None if not present). 

1018 

1019 If the XML-RPC packet represents a fault condition, this function 

1020 raises a Fault exception. 

1021 """ 

1022 p, u = getparser(use_datetime=use_datetime, use_builtin_types=use_builtin_types) 

1023 p.feed(data) 

1024 p.close() 

1025 return u.close(), u.getmethodname() 

1026 

1027## 

1028# Encode a string using the gzip content encoding such as specified by the 

1029# Content-Encoding: gzip 

1030# in the HTTP header, as described in RFC 1952 

1031# 

1032# @param data the unencoded data 

1033# @return the encoded data 

1034 

1035def gzip_encode(data): 

1036 """data -> gzip encoded data 

1037 

1038 Encode data using the gzip content encoding as described in RFC 1952 

1039 """ 

1040 if not gzip: 

1041 raise NotImplementedError 

1042 f = BytesIO() 

1043 with gzip.GzipFile(mode="wb", fileobj=f, compresslevel=1) as gzf: 

1044 gzf.write(data) 

1045 return f.getvalue() 

1046 

1047## 

1048# Decode a string using the gzip content encoding such as specified by the 

1049# Content-Encoding: gzip 

1050# in the HTTP header, as described in RFC 1952 

1051# 

1052# @param data The encoded data 

1053# @keyparam max_decode Maximum bytes to decode (20 MiB default), use negative 

1054# values for unlimited decoding 

1055# @return the unencoded data 

1056# @raises ValueError if data is not correctly coded. 

1057# @raises ValueError if max gzipped payload length exceeded 

1058 

1059def gzip_decode(data, max_decode=20971520): 

1060 """gzip encoded data -> unencoded data 

1061 

1062 Decode data using the gzip content encoding as described in RFC 1952 

1063 """ 

1064 if not gzip: 

1065 raise NotImplementedError 

1066 with gzip.GzipFile(mode="rb", fileobj=BytesIO(data)) as gzf: 

1067 try: 

1068 if max_decode < 0: # no limit 

1069 decoded = gzf.read() 

1070 else: 

1071 decoded = gzf.read(max_decode + 1) 

1072 except OSError: 

1073 raise ValueError("invalid data") 

1074 if max_decode >= 0 and len(decoded) > max_decode: 

1075 raise ValueError("max gzipped payload length exceeded") 

1076 return decoded 

1077 

1078## 

1079# Return a decoded file-like object for the gzip encoding 

1080# as described in RFC 1952. 

1081# 

1082# @param response A stream supporting a read() method 

1083# @return a file-like object that the decoded data can be read() from 

1084 

1085class GzipDecodedResponse(gzip.GzipFile if gzip else object): 

1086 """a file-like object to decode a response encoded with the gzip 

1087 method, as described in RFC 1952. 

1088 """ 

1089 def __init__(self, response): 

1090 #response doesn't support tell() and read(), required by 

1091 #GzipFile 

1092 if not gzip: 

1093 raise NotImplementedError 

1094 self.io = BytesIO(response.read()) 

1095 gzip.GzipFile.__init__(self, mode="rb", fileobj=self.io) 

1096 

1097 def close(self): 

1098 try: 

1099 gzip.GzipFile.close(self) 

1100 finally: 

1101 self.io.close() 

1102 

1103 

1104# -------------------------------------------------------------------- 

1105# request dispatcher 

1106 

1107class _Method: 

1108 # some magic to bind an XML-RPC method to an RPC server. 

1109 # supports "nested" methods (e.g. examples.getStateName) 

1110 def __init__(self, send, name): 

1111 self.__send = send 

1112 self.__name = name 

1113 def __getattr__(self, name): 

1114 return _Method(self.__send, "%s.%s" % (self.__name, name)) 

1115 def __call__(self, *args): 

1116 return self.__send(self.__name, args) 

1117 

1118## 

1119# Standard transport class for XML-RPC over HTTP. 

1120# <p> 

1121# You can create custom transports by subclassing this method, and 

1122# overriding selected methods. 

1123 

1124class Transport: 

1125 """Handles an HTTP transaction to an XML-RPC server.""" 

1126 

1127 # client identifier (may be overridden) 

1128 user_agent = "Python-xmlrpc/%s" % __version__ 

1129 

1130 #if true, we'll request gzip encoding 

1131 accept_gzip_encoding = True 

1132 

1133 # if positive, encode request using gzip if it exceeds this threshold 

1134 # note that many servers will get confused, so only use it if you know 

1135 # that they can decode such a request 

1136 encode_threshold = None #None = don't encode 

1137 

1138 def __init__(self, use_datetime=False, use_builtin_types=False, 

1139 *, headers=()): 

1140 self._use_datetime = use_datetime 

1141 self._use_builtin_types = use_builtin_types 

1142 self._connection = (None, None) 

1143 self._headers = list(headers) 

1144 self._extra_headers = [] 

1145 

1146 ## 

1147 # Send a complete request, and parse the response. 

1148 # Retry request if a cached connection has disconnected. 

1149 # 

1150 # @param host Target host. 

1151 # @param handler Target PRC handler. 

1152 # @param request_body XML-RPC request body. 

1153 # @param verbose Debugging flag. 

1154 # @return Parsed response. 

1155 

1156 def request(self, host, handler, request_body, verbose=False): 

1157 #retry request once if cached connection has gone cold 

1158 for i in (0, 1): 

1159 try: 

1160 return self.single_request(host, handler, request_body, verbose) 

1161 except http.client.RemoteDisconnected: 

1162 if i: 

1163 raise 

1164 except OSError as e: 

1165 if i or e.errno not in (errno.ECONNRESET, errno.ECONNABORTED, 

1166 errno.EPIPE): 

1167 raise 

1168 

1169 def single_request(self, host, handler, request_body, verbose=False): 

1170 # issue XML-RPC request 

1171 try: 

1172 http_conn = self.send_request(host, handler, request_body, verbose) 

1173 resp = http_conn.getresponse() 

1174 if resp.status == 200: 

1175 self.verbose = verbose 

1176 return self.parse_response(resp) 

1177 

1178 except Fault: 

1179 raise 

1180 except Exception: 

1181 #All unexpected errors leave connection in 

1182 # a strange state, so we clear it. 

1183 self.close() 

1184 raise 

1185 

1186 #We got an error response. 

1187 #Discard any response data and raise exception 

1188 if resp.getheader("content-length", ""): 

1189 resp.read() 

1190 raise ProtocolError( 

1191 host + handler, 

1192 resp.status, resp.reason, 

1193 dict(resp.getheaders()) 

1194 ) 

1195 

1196 

1197 ## 

1198 # Create parser. 

1199 # 

1200 # @return A 2-tuple containing a parser and an unmarshaller. 

1201 

1202 def getparser(self): 

1203 # get parser and unmarshaller 

1204 return getparser(use_datetime=self._use_datetime, 

1205 use_builtin_types=self._use_builtin_types) 

1206 

1207 ## 

1208 # Get authorization info from host parameter 

1209 # Host may be a string, or a (host, x509-dict) tuple; if a string, 

1210 # it is checked for a "user:pw@host" format, and a "Basic 

1211 # Authentication" header is added if appropriate. 

1212 # 

1213 # @param host Host descriptor (URL or (URL, x509 info) tuple). 

1214 # @return A 3-tuple containing (actual host, extra headers, 

1215 # x509 info). The header and x509 fields may be None. 

1216 

1217 def get_host_info(self, host): 

1218 

1219 x509 = {} 

1220 if isinstance(host, tuple): 

1221 host, x509 = host 

1222 

1223 auth, host = urllib.parse._splituser(host) 

1224 

1225 if auth: 

1226 auth = urllib.parse.unquote_to_bytes(auth) 

1227 auth = base64.encodebytes(auth).decode("utf-8") 

1228 auth = "".join(auth.split()) # get rid of whitespace 

1229 extra_headers = [ 

1230 ("Authorization", "Basic " + auth) 

1231 ] 

1232 else: 

1233 extra_headers = [] 

1234 

1235 return host, extra_headers, x509 

1236 

1237 ## 

1238 # Connect to server. 

1239 # 

1240 # @param host Target host. 

1241 # @return An HTTPConnection object 

1242 

1243 def make_connection(self, host): 

1244 #return an existing connection if possible. This allows 

1245 #HTTP/1.1 keep-alive. 

1246 if self._connection and host == self._connection[0]: 

1247 return self._connection[1] 

1248 # create a HTTP connection object from a host descriptor 

1249 chost, self._extra_headers, x509 = self.get_host_info(host) 

1250 self._connection = host, http.client.HTTPConnection(chost) 

1251 return self._connection[1] 

1252 

1253 ## 

1254 # Clear any cached connection object. 

1255 # Used in the event of socket errors. 

1256 # 

1257 def close(self): 

1258 host, connection = self._connection 

1259 if connection: 

1260 self._connection = (None, None) 

1261 connection.close() 

1262 

1263 ## 

1264 # Send HTTP request. 

1265 # 

1266 # @param host Host descriptor (URL or (URL, x509 info) tuple). 

1267 # @param handler Target RPC handler (a path relative to host) 

1268 # @param request_body The XML-RPC request body 

1269 # @param debug Enable debugging if debug is true. 

1270 # @return An HTTPConnection. 

1271 

1272 def send_request(self, host, handler, request_body, debug): 

1273 connection = self.make_connection(host) 

1274 headers = self._headers + self._extra_headers 

1275 if debug: 

1276 connection.set_debuglevel(1) 

1277 if self.accept_gzip_encoding and gzip: 

1278 connection.putrequest("POST", handler, skip_accept_encoding=True) 

1279 headers.append(("Accept-Encoding", "gzip")) 

1280 else: 

1281 connection.putrequest("POST", handler) 

1282 headers.append(("Content-Type", "text/xml")) 

1283 headers.append(("User-Agent", self.user_agent)) 

1284 self.send_headers(connection, headers) 

1285 self.send_content(connection, request_body) 

1286 return connection 

1287 

1288 ## 

1289 # Send request headers. 

1290 # This function provides a useful hook for subclassing 

1291 # 

1292 # @param connection httpConnection. 

1293 # @param headers list of key,value pairs for HTTP headers 

1294 

1295 def send_headers(self, connection, headers): 

1296 for key, val in headers: 

1297 connection.putheader(key, val) 

1298 

1299 ## 

1300 # Send request body. 

1301 # This function provides a useful hook for subclassing 

1302 # 

1303 # @param connection httpConnection. 

1304 # @param request_body XML-RPC request body. 

1305 

1306 def send_content(self, connection, request_body): 

1307 #optionally encode the request 

1308 if (self.encode_threshold is not None and 

1309 self.encode_threshold < len(request_body) and 

1310 gzip): 

1311 connection.putheader("Content-Encoding", "gzip") 

1312 request_body = gzip_encode(request_body) 

1313 

1314 connection.putheader("Content-Length", str(len(request_body))) 

1315 connection.endheaders(request_body) 

1316 

1317 ## 

1318 # Parse response. 

1319 # 

1320 # @param file Stream. 

1321 # @return Response tuple and target method. 

1322 

1323 def parse_response(self, response): 

1324 # read response data from httpresponse, and parse it 

1325 # Check for new http response object, otherwise it is a file object. 

1326 if hasattr(response, 'getheader'): 

1327 if response.getheader("Content-Encoding", "") == "gzip": 

1328 stream = GzipDecodedResponse(response) 

1329 else: 

1330 stream = response 

1331 else: 

1332 stream = response 

1333 

1334 p, u = self.getparser() 

1335 

1336 while 1: 

1337 data = stream.read(1024) 

1338 if not data: 

1339 break 

1340 if self.verbose: 

1341 print("body:", repr(data)) 

1342 p.feed(data) 

1343 

1344 if stream is not response: 

1345 stream.close() 

1346 p.close() 

1347 

1348 return u.close() 

1349 

1350## 

1351# Standard transport class for XML-RPC over HTTPS. 

1352 

1353class SafeTransport(Transport): 

1354 """Handles an HTTPS transaction to an XML-RPC server.""" 

1355 

1356 def __init__(self, use_datetime=False, use_builtin_types=False, 

1357 *, headers=(), context=None): 

1358 super().__init__(use_datetime=use_datetime, 

1359 use_builtin_types=use_builtin_types, 

1360 headers=headers) 

1361 self.context = context 

1362 

1363 # FIXME: mostly untested 

1364 

1365 def make_connection(self, host): 

1366 if self._connection and host == self._connection[0]: 

1367 return self._connection[1] 

1368 

1369 if not hasattr(http.client, "HTTPSConnection"): 

1370 raise NotImplementedError( 

1371 "your version of http.client doesn't support HTTPS") 

1372 # create a HTTPS connection object from a host descriptor 

1373 # host may be a string, or a (host, x509-dict) tuple 

1374 chost, self._extra_headers, x509 = self.get_host_info(host) 

1375 self._connection = host, http.client.HTTPSConnection(chost, 

1376 None, context=self.context, **(x509 or {})) 

1377 return self._connection[1] 

1378 

1379## 

1380# Standard server proxy. This class establishes a virtual connection 

1381# to an XML-RPC server. 

1382# <p> 

1383# This class is available as ServerProxy and Server. New code should 

1384# use ServerProxy, to avoid confusion. 

1385# 

1386# @def ServerProxy(uri, **options) 

1387# @param uri The connection point on the server. 

1388# @keyparam transport A transport factory, compatible with the 

1389# standard transport class. 

1390# @keyparam encoding The default encoding used for 8-bit strings 

1391# (default is UTF-8). 

1392# @keyparam verbose Use a true value to enable debugging output. 

1393# (printed to standard output). 

1394# @see Transport 

1395 

1396class ServerProxy: 

1397 """uri [,options] -> a logical connection to an XML-RPC server 

1398 

1399 uri is the connection point on the server, given as 

1400 scheme://host/target. 

1401 

1402 The standard implementation always supports the "http" scheme. If 

1403 SSL socket support is available (Python 2.0), it also supports 

1404 "https". 

1405 

1406 If the target part and the slash preceding it are both omitted, 

1407 "/RPC2" is assumed. 

1408 

1409 The following options can be given as keyword arguments: 

1410 

1411 transport: a transport factory 

1412 encoding: the request encoding (default is UTF-8) 

1413 

1414 All 8-bit strings passed to the server proxy are assumed to use 

1415 the given encoding. 

1416 """ 

1417 

1418 def __init__(self, uri, transport=None, encoding=None, verbose=False, 

1419 allow_none=False, use_datetime=False, use_builtin_types=False, 

1420 *, headers=(), context=None): 

1421 # establish a "logical" server connection 

1422 

1423 # get the url 

1424 p = urllib.parse.urlsplit(uri) 

1425 if p.scheme not in ("http", "https"): 

1426 raise OSError("unsupported XML-RPC protocol") 

1427 self.__host = p.netloc 

1428 self.__handler = urllib.parse.urlunsplit(["", "", *p[2:]]) 

1429 if not self.__handler: 

1430 self.__handler = "/RPC2" 

1431 

1432 if transport is None: 

1433 if p.scheme == "https": 

1434 handler = SafeTransport 

1435 extra_kwargs = {"context": context} 

1436 else: 

1437 handler = Transport 

1438 extra_kwargs = {} 

1439 transport = handler(use_datetime=use_datetime, 

1440 use_builtin_types=use_builtin_types, 

1441 headers=headers, 

1442 **extra_kwargs) 

1443 self.__transport = transport 

1444 

1445 self.__encoding = encoding or 'utf-8' 

1446 self.__verbose = verbose 

1447 self.__allow_none = allow_none 

1448 

1449 def __close(self): 

1450 self.__transport.close() 

1451 

1452 def __request(self, methodname, params): 

1453 # call a method on the remote server 

1454 

1455 request = dumps(params, methodname, encoding=self.__encoding, 

1456 allow_none=self.__allow_none).encode(self.__encoding, 'xmlcharrefreplace') 

1457 

1458 response = self.__transport.request( 

1459 self.__host, 

1460 self.__handler, 

1461 request, 

1462 verbose=self.__verbose 

1463 ) 

1464 

1465 if len(response) == 1: 

1466 response = response[0] 

1467 

1468 return response 

1469 

1470 def __repr__(self): 

1471 return ( 

1472 "<%s for %s%s>" % 

1473 (self.__class__.__name__, self.__host, self.__handler) 

1474 ) 

1475 

1476 def __getattr__(self, name): 

1477 # magic method dispatcher 

1478 return _Method(self.__request, name) 

1479 

1480 # note: to call a remote object with a non-standard name, use 

1481 # result getattr(server, "strange-python-name")(args) 

1482 

1483 def __call__(self, attr): 

1484 """A workaround to get special attributes on the ServerProxy 

1485 without interfering with the magic __getattr__ 

1486 """ 

1487 if attr == "close": 

1488 return self.__close 

1489 elif attr == "transport": 

1490 return self.__transport 

1491 raise AttributeError("Attribute %r not found" % (attr,)) 

1492 

1493 def __enter__(self): 

1494 return self 

1495 

1496 def __exit__(self, *args): 

1497 self.__close() 

1498 

1499# compatibility 

1500 

1501Server = ServerProxy 

1502 

1503# -------------------------------------------------------------------- 

1504# test code 

1505 

1506if __name__ == "__main__": 

1507 

1508 # simple test program (from the XML-RPC specification) 

1509 

1510 # local server, available from Lib/xmlrpc/server.py 

1511 server = ServerProxy("http://localhost:8000") 

1512 

1513 try: 

1514 print(server.currentTime.getCurrentTime()) 

1515 except Error as v: 

1516 print("ERROR", v) 

1517 

1518 multi = MultiCall(server) 

1519 multi.getData() 

1520 multi.pow(2,9) 

1521 multi.add(1,2) 

1522 try: 

1523 for response in multi(): 

1524 print(response) 

1525 except Error as v: 

1526 print("ERROR", v)