|
1 # |
|
2 # XML-RPC CLIENT LIBRARY |
|
3 # $Id: xmlrpclib.py 65467 2008-08-04 00:50:11Z brett.cannon $ |
|
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 # |
|
53 # Copyright (c) 1999-2002 by Secret Labs AB. |
|
54 # Copyright (c) 1999-2002 by Fredrik Lundh. |
|
55 # |
|
56 # info@pythonware.com |
|
57 # http://www.pythonware.com |
|
58 # |
|
59 # -------------------------------------------------------------------- |
|
60 # The XML-RPC client interface is |
|
61 # |
|
62 # Copyright (c) 1999-2002 by Secret Labs AB |
|
63 # Copyright (c) 1999-2002 by Fredrik Lundh |
|
64 # |
|
65 # By obtaining, using, and/or copying this software and/or its |
|
66 # associated documentation, you agree that you have read, understood, |
|
67 # and will comply with the following terms and conditions: |
|
68 # |
|
69 # Permission to use, copy, modify, and distribute this software and |
|
70 # its associated documentation for any purpose and without fee is |
|
71 # hereby granted, provided that the above copyright notice appears in |
|
72 # all copies, and that both that copyright notice and this permission |
|
73 # notice appear in supporting documentation, and that the name of |
|
74 # Secret Labs AB or the author not be used in advertising or publicity |
|
75 # pertaining to distribution of the software without specific, written |
|
76 # prior permission. |
|
77 # |
|
78 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD |
|
79 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT- |
|
80 # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR |
|
81 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY |
|
82 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
|
83 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS |
|
84 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE |
|
85 # OF THIS SOFTWARE. |
|
86 # -------------------------------------------------------------------- |
|
87 |
|
88 # |
|
89 # things to look into some day: |
|
90 |
|
91 # TODO: sort out True/False/boolean issues for Python 2.3 |
|
92 |
|
93 """ |
|
94 An XML-RPC client interface for Python. |
|
95 |
|
96 The marshalling and response parser code can also be used to |
|
97 implement XML-RPC servers. |
|
98 |
|
99 Exported exceptions: |
|
100 |
|
101 Error Base class for client errors |
|
102 ProtocolError Indicates an HTTP protocol error |
|
103 ResponseError Indicates a broken response package |
|
104 Fault Indicates an XML-RPC fault package |
|
105 |
|
106 Exported classes: |
|
107 |
|
108 ServerProxy Represents a logical connection to an XML-RPC server |
|
109 |
|
110 MultiCall Executor of boxcared xmlrpc requests |
|
111 Boolean boolean wrapper to generate a "boolean" XML-RPC value |
|
112 DateTime dateTime wrapper for an ISO 8601 string or time tuple or |
|
113 localtime integer value to generate a "dateTime.iso8601" |
|
114 XML-RPC value |
|
115 Binary binary data wrapper |
|
116 |
|
117 SlowParser Slow but safe standard parser (based on xmllib) |
|
118 Marshaller Generate an XML-RPC params chunk from a Python data structure |
|
119 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message |
|
120 Transport Handles an HTTP transaction to an XML-RPC server |
|
121 SafeTransport Handles an HTTPS transaction to an XML-RPC server |
|
122 |
|
123 Exported constants: |
|
124 |
|
125 True |
|
126 False |
|
127 |
|
128 Exported functions: |
|
129 |
|
130 boolean Convert any Python value to an XML-RPC boolean |
|
131 getparser Create instance of the fastest available parser & attach |
|
132 to an unmarshalling object |
|
133 dumps Convert an argument tuple or a Fault instance to an XML-RPC |
|
134 request (or response, if the methodresponse option is used). |
|
135 loads Convert an XML-RPC packet to unmarshalled data plus a method |
|
136 name (None if not present). |
|
137 """ |
|
138 |
|
139 import re, string, time, operator |
|
140 |
|
141 from types import * |
|
142 |
|
143 # -------------------------------------------------------------------- |
|
144 # Internal stuff |
|
145 |
|
146 try: |
|
147 unicode |
|
148 except NameError: |
|
149 unicode = None # unicode support not available |
|
150 |
|
151 try: |
|
152 import datetime |
|
153 except ImportError: |
|
154 datetime = None |
|
155 |
|
156 try: |
|
157 _bool_is_builtin = False.__class__.__name__ == "bool" |
|
158 except NameError: |
|
159 _bool_is_builtin = 0 |
|
160 |
|
161 def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search): |
|
162 # decode non-ascii string (if possible) |
|
163 if unicode and encoding and is8bit(data): |
|
164 data = unicode(data, encoding) |
|
165 return data |
|
166 |
|
167 def escape(s, replace=string.replace): |
|
168 s = replace(s, "&", "&") |
|
169 s = replace(s, "<", "<") |
|
170 return replace(s, ">", ">",) |
|
171 |
|
172 if unicode: |
|
173 def _stringify(string): |
|
174 # convert to 7-bit ascii if possible |
|
175 try: |
|
176 return string.encode("ascii") |
|
177 except UnicodeError: |
|
178 return string |
|
179 else: |
|
180 def _stringify(string): |
|
181 return string |
|
182 |
|
183 __version__ = "1.0.1" |
|
184 |
|
185 # xmlrpc integer limits |
|
186 MAXINT = 2L**31-1 |
|
187 MININT = -2L**31 |
|
188 |
|
189 # -------------------------------------------------------------------- |
|
190 # Error constants (from Dan Libby's specification at |
|
191 # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php) |
|
192 |
|
193 # Ranges of errors |
|
194 PARSE_ERROR = -32700 |
|
195 SERVER_ERROR = -32600 |
|
196 APPLICATION_ERROR = -32500 |
|
197 SYSTEM_ERROR = -32400 |
|
198 TRANSPORT_ERROR = -32300 |
|
199 |
|
200 # Specific errors |
|
201 NOT_WELLFORMED_ERROR = -32700 |
|
202 UNSUPPORTED_ENCODING = -32701 |
|
203 INVALID_ENCODING_CHAR = -32702 |
|
204 INVALID_XMLRPC = -32600 |
|
205 METHOD_NOT_FOUND = -32601 |
|
206 INVALID_METHOD_PARAMS = -32602 |
|
207 INTERNAL_ERROR = -32603 |
|
208 |
|
209 # -------------------------------------------------------------------- |
|
210 # Exceptions |
|
211 |
|
212 ## |
|
213 # Base class for all kinds of client-side errors. |
|
214 |
|
215 class Error(Exception): |
|
216 """Base class for client errors.""" |
|
217 def __str__(self): |
|
218 return repr(self) |
|
219 |
|
220 ## |
|
221 # Indicates an HTTP-level protocol error. This is raised by the HTTP |
|
222 # transport layer, if the server returns an error code other than 200 |
|
223 # (OK). |
|
224 # |
|
225 # @param url The target URL. |
|
226 # @param errcode The HTTP error code. |
|
227 # @param errmsg The HTTP error message. |
|
228 # @param headers The HTTP header dictionary. |
|
229 |
|
230 class ProtocolError(Error): |
|
231 """Indicates an HTTP protocol error.""" |
|
232 def __init__(self, url, errcode, errmsg, headers): |
|
233 Error.__init__(self) |
|
234 self.url = url |
|
235 self.errcode = errcode |
|
236 self.errmsg = errmsg |
|
237 self.headers = headers |
|
238 def __repr__(self): |
|
239 return ( |
|
240 "<ProtocolError for %s: %s %s>" % |
|
241 (self.url, self.errcode, self.errmsg) |
|
242 ) |
|
243 |
|
244 ## |
|
245 # Indicates a broken XML-RPC response package. This exception is |
|
246 # raised by the unmarshalling layer, if the XML-RPC response is |
|
247 # malformed. |
|
248 |
|
249 class ResponseError(Error): |
|
250 """Indicates a broken response package.""" |
|
251 pass |
|
252 |
|
253 ## |
|
254 # Indicates an XML-RPC fault response package. This exception is |
|
255 # raised by the unmarshalling layer, if the XML-RPC response contains |
|
256 # a fault string. This exception can also used as a class, to |
|
257 # generate a fault XML-RPC message. |
|
258 # |
|
259 # @param faultCode The XML-RPC fault code. |
|
260 # @param faultString The XML-RPC fault string. |
|
261 |
|
262 class Fault(Error): |
|
263 """Indicates an XML-RPC fault package.""" |
|
264 def __init__(self, faultCode, faultString, **extra): |
|
265 Error.__init__(self) |
|
266 self.faultCode = faultCode |
|
267 self.faultString = faultString |
|
268 def __repr__(self): |
|
269 return ( |
|
270 "<Fault %s: %s>" % |
|
271 (self.faultCode, repr(self.faultString)) |
|
272 ) |
|
273 |
|
274 # -------------------------------------------------------------------- |
|
275 # Special values |
|
276 |
|
277 ## |
|
278 # Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and |
|
279 # xmlrpclib.False constants, or the xmlrpclib.boolean() function, to |
|
280 # generate boolean XML-RPC values. |
|
281 # |
|
282 # @param value A boolean value. Any true value is interpreted as True, |
|
283 # all other values are interpreted as False. |
|
284 |
|
285 from sys import modules |
|
286 mod_dict = modules[__name__].__dict__ |
|
287 if _bool_is_builtin: |
|
288 boolean = Boolean = bool |
|
289 # to avoid breaking code which references xmlrpclib.{True,False} |
|
290 mod_dict['True'] = True |
|
291 mod_dict['False'] = False |
|
292 else: |
|
293 class Boolean: |
|
294 """Boolean-value wrapper. |
|
295 |
|
296 Use True or False to generate a "boolean" XML-RPC value. |
|
297 """ |
|
298 |
|
299 def __init__(self, value = 0): |
|
300 self.value = operator.truth(value) |
|
301 |
|
302 def encode(self, out): |
|
303 out.write("<value><boolean>%d</boolean></value>\n" % self.value) |
|
304 |
|
305 def __cmp__(self, other): |
|
306 if isinstance(other, Boolean): |
|
307 other = other.value |
|
308 return cmp(self.value, other) |
|
309 |
|
310 def __repr__(self): |
|
311 if self.value: |
|
312 return "<Boolean True at %x>" % id(self) |
|
313 else: |
|
314 return "<Boolean False at %x>" % id(self) |
|
315 |
|
316 def __int__(self): |
|
317 return self.value |
|
318 |
|
319 def __nonzero__(self): |
|
320 return self.value |
|
321 |
|
322 mod_dict['True'] = Boolean(1) |
|
323 mod_dict['False'] = Boolean(0) |
|
324 |
|
325 ## |
|
326 # Map true or false value to XML-RPC boolean values. |
|
327 # |
|
328 # @def boolean(value) |
|
329 # @param value A boolean value. Any true value is mapped to True, |
|
330 # all other values are mapped to False. |
|
331 # @return xmlrpclib.True or xmlrpclib.False. |
|
332 # @see Boolean |
|
333 # @see True |
|
334 # @see False |
|
335 |
|
336 def boolean(value, _truefalse=(False, True)): |
|
337 """Convert any Python value to XML-RPC 'boolean'.""" |
|
338 return _truefalse[operator.truth(value)] |
|
339 |
|
340 del modules, mod_dict |
|
341 |
|
342 ## |
|
343 # Wrapper for XML-RPC DateTime values. This converts a time value to |
|
344 # the format used by XML-RPC. |
|
345 # <p> |
|
346 # The value can be given as a string in the format |
|
347 # "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by |
|
348 # time.localtime()), or an integer value (as returned by time.time()). |
|
349 # The wrapper uses time.localtime() to convert an integer to a time |
|
350 # tuple. |
|
351 # |
|
352 # @param value The time, given as an ISO 8601 string, a time |
|
353 # tuple, or a integer time value. |
|
354 |
|
355 def _strftime(value): |
|
356 if datetime: |
|
357 if isinstance(value, datetime.datetime): |
|
358 return "%04d%02d%02dT%02d:%02d:%02d" % ( |
|
359 value.year, value.month, value.day, |
|
360 value.hour, value.minute, value.second) |
|
361 |
|
362 if not isinstance(value, (TupleType, time.struct_time)): |
|
363 if value == 0: |
|
364 value = time.time() |
|
365 value = time.localtime(value) |
|
366 |
|
367 return "%04d%02d%02dT%02d:%02d:%02d" % value[:6] |
|
368 |
|
369 class DateTime: |
|
370 """DateTime wrapper for an ISO 8601 string or time tuple or |
|
371 localtime integer value to generate 'dateTime.iso8601' XML-RPC |
|
372 value. |
|
373 """ |
|
374 |
|
375 def __init__(self, value=0): |
|
376 if isinstance(value, StringType): |
|
377 self.value = value |
|
378 else: |
|
379 self.value = _strftime(value) |
|
380 |
|
381 def make_comparable(self, other): |
|
382 if isinstance(other, DateTime): |
|
383 s = self.value |
|
384 o = other.value |
|
385 elif datetime and isinstance(other, datetime.datetime): |
|
386 s = self.value |
|
387 o = other.strftime("%Y%m%dT%H:%M:%S") |
|
388 elif isinstance(other, (str, unicode)): |
|
389 s = self.value |
|
390 o = other |
|
391 elif hasattr(other, "timetuple"): |
|
392 s = self.timetuple() |
|
393 o = other.timetuple() |
|
394 else: |
|
395 otype = (hasattr(other, "__class__") |
|
396 and other.__class__.__name__ |
|
397 or type(other)) |
|
398 raise TypeError("Can't compare %s and %s" % |
|
399 (self.__class__.__name__, otype)) |
|
400 return s, o |
|
401 |
|
402 def __lt__(self, other): |
|
403 s, o = self.make_comparable(other) |
|
404 return s < o |
|
405 |
|
406 def __le__(self, other): |
|
407 s, o = self.make_comparable(other) |
|
408 return s <= o |
|
409 |
|
410 def __gt__(self, other): |
|
411 s, o = self.make_comparable(other) |
|
412 return s > o |
|
413 |
|
414 def __ge__(self, other): |
|
415 s, o = self.make_comparable(other) |
|
416 return s >= o |
|
417 |
|
418 def __eq__(self, other): |
|
419 s, o = self.make_comparable(other) |
|
420 return s == o |
|
421 |
|
422 def __ne__(self, other): |
|
423 s, o = self.make_comparable(other) |
|
424 return s != o |
|
425 |
|
426 def timetuple(self): |
|
427 return time.strptime(self.value, "%Y%m%dT%H:%M:%S") |
|
428 |
|
429 def __cmp__(self, other): |
|
430 s, o = self.make_comparable(other) |
|
431 return cmp(s, o) |
|
432 |
|
433 ## |
|
434 # Get date/time value. |
|
435 # |
|
436 # @return Date/time value, as an ISO 8601 string. |
|
437 |
|
438 def __str__(self): |
|
439 return self.value |
|
440 |
|
441 def __repr__(self): |
|
442 return "<DateTime %s at %x>" % (repr(self.value), id(self)) |
|
443 |
|
444 def decode(self, data): |
|
445 data = str(data) |
|
446 self.value = string.strip(data) |
|
447 |
|
448 def encode(self, out): |
|
449 out.write("<value><dateTime.iso8601>") |
|
450 out.write(self.value) |
|
451 out.write("</dateTime.iso8601></value>\n") |
|
452 |
|
453 def _datetime(data): |
|
454 # decode xml element contents into a DateTime structure. |
|
455 value = DateTime() |
|
456 value.decode(data) |
|
457 return value |
|
458 |
|
459 def _datetime_type(data): |
|
460 t = time.strptime(data, "%Y%m%dT%H:%M:%S") |
|
461 return datetime.datetime(*tuple(t)[:6]) |
|
462 |
|
463 ## |
|
464 # Wrapper for binary data. This can be used to transport any kind |
|
465 # of binary data over XML-RPC, using BASE64 encoding. |
|
466 # |
|
467 # @param data An 8-bit string containing arbitrary data. |
|
468 |
|
469 import base64 |
|
470 try: |
|
471 import cStringIO as StringIO |
|
472 except ImportError: |
|
473 import StringIO |
|
474 |
|
475 class Binary: |
|
476 """Wrapper for binary data.""" |
|
477 |
|
478 def __init__(self, data=None): |
|
479 self.data = data |
|
480 |
|
481 ## |
|
482 # Get buffer contents. |
|
483 # |
|
484 # @return Buffer contents, as an 8-bit string. |
|
485 |
|
486 def __str__(self): |
|
487 return self.data or "" |
|
488 |
|
489 def __cmp__(self, other): |
|
490 if isinstance(other, Binary): |
|
491 other = other.data |
|
492 return cmp(self.data, other) |
|
493 |
|
494 def decode(self, data): |
|
495 self.data = base64.decodestring(data) |
|
496 |
|
497 def encode(self, out): |
|
498 out.write("<value><base64>\n") |
|
499 base64.encode(StringIO.StringIO(self.data), out) |
|
500 out.write("</base64></value>\n") |
|
501 |
|
502 def _binary(data): |
|
503 # decode xml element contents into a Binary structure |
|
504 value = Binary() |
|
505 value.decode(data) |
|
506 return value |
|
507 |
|
508 WRAPPERS = (DateTime, Binary) |
|
509 if not _bool_is_builtin: |
|
510 WRAPPERS = WRAPPERS + (Boolean,) |
|
511 |
|
512 # -------------------------------------------------------------------- |
|
513 # XML parsers |
|
514 |
|
515 try: |
|
516 # optional xmlrpclib accelerator |
|
517 import _xmlrpclib |
|
518 FastParser = _xmlrpclib.Parser |
|
519 FastUnmarshaller = _xmlrpclib.Unmarshaller |
|
520 except (AttributeError, ImportError): |
|
521 FastParser = FastUnmarshaller = None |
|
522 |
|
523 try: |
|
524 import _xmlrpclib |
|
525 FastMarshaller = _xmlrpclib.Marshaller |
|
526 except (AttributeError, ImportError): |
|
527 FastMarshaller = None |
|
528 |
|
529 # |
|
530 # the SGMLOP parser is about 15x faster than Python's builtin |
|
531 # XML parser. SGMLOP sources can be downloaded from: |
|
532 # |
|
533 # http://www.pythonware.com/products/xml/sgmlop.htm |
|
534 # |
|
535 |
|
536 try: |
|
537 import sgmlop |
|
538 if not hasattr(sgmlop, "XMLParser"): |
|
539 raise ImportError |
|
540 except ImportError: |
|
541 SgmlopParser = None # sgmlop accelerator not available |
|
542 else: |
|
543 class SgmlopParser: |
|
544 def __init__(self, target): |
|
545 |
|
546 # setup callbacks |
|
547 self.finish_starttag = target.start |
|
548 self.finish_endtag = target.end |
|
549 self.handle_data = target.data |
|
550 self.handle_xml = target.xml |
|
551 |
|
552 # activate parser |
|
553 self.parser = sgmlop.XMLParser() |
|
554 self.parser.register(self) |
|
555 self.feed = self.parser.feed |
|
556 self.entity = { |
|
557 "amp": "&", "gt": ">", "lt": "<", |
|
558 "apos": "'", "quot": '"' |
|
559 } |
|
560 |
|
561 def close(self): |
|
562 try: |
|
563 self.parser.close() |
|
564 finally: |
|
565 self.parser = self.feed = None # nuke circular reference |
|
566 |
|
567 def handle_proc(self, tag, attr): |
|
568 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr) |
|
569 if m: |
|
570 self.handle_xml(m.group(1), 1) |
|
571 |
|
572 def handle_entityref(self, entity): |
|
573 # <string> entity |
|
574 try: |
|
575 self.handle_data(self.entity[entity]) |
|
576 except KeyError: |
|
577 self.handle_data("&%s;" % entity) |
|
578 |
|
579 try: |
|
580 from xml.parsers import expat |
|
581 if not hasattr(expat, "ParserCreate"): |
|
582 raise ImportError |
|
583 except ImportError: |
|
584 ExpatParser = None # expat not available |
|
585 else: |
|
586 class ExpatParser: |
|
587 # fast expat parser for Python 2.0 and later. this is about |
|
588 # 50% slower than sgmlop, on roundtrip testing |
|
589 def __init__(self, target): |
|
590 self._parser = parser = expat.ParserCreate(None, None) |
|
591 self._target = target |
|
592 parser.StartElementHandler = target.start |
|
593 parser.EndElementHandler = target.end |
|
594 parser.CharacterDataHandler = target.data |
|
595 encoding = None |
|
596 if not parser.returns_unicode: |
|
597 encoding = "utf-8" |
|
598 target.xml(encoding, None) |
|
599 |
|
600 def feed(self, data): |
|
601 self._parser.Parse(data, 0) |
|
602 |
|
603 def close(self): |
|
604 self._parser.Parse("", 1) # end of data |
|
605 del self._target, self._parser # get rid of circular references |
|
606 |
|
607 class SlowParser: |
|
608 """Default XML parser (based on xmllib.XMLParser).""" |
|
609 # this is about 10 times slower than sgmlop, on roundtrip |
|
610 # testing. |
|
611 def __init__(self, target): |
|
612 import xmllib # lazy subclassing (!) |
|
613 if xmllib.XMLParser not in SlowParser.__bases__: |
|
614 SlowParser.__bases__ = (xmllib.XMLParser,) |
|
615 self.handle_xml = target.xml |
|
616 self.unknown_starttag = target.start |
|
617 self.handle_data = target.data |
|
618 self.handle_cdata = target.data |
|
619 self.unknown_endtag = target.end |
|
620 try: |
|
621 xmllib.XMLParser.__init__(self, accept_utf8=1) |
|
622 except TypeError: |
|
623 xmllib.XMLParser.__init__(self) # pre-2.0 |
|
624 |
|
625 # -------------------------------------------------------------------- |
|
626 # XML-RPC marshalling and unmarshalling code |
|
627 |
|
628 ## |
|
629 # XML-RPC marshaller. |
|
630 # |
|
631 # @param encoding Default encoding for 8-bit strings. The default |
|
632 # value is None (interpreted as UTF-8). |
|
633 # @see dumps |
|
634 |
|
635 class Marshaller: |
|
636 """Generate an XML-RPC params chunk from a Python data structure. |
|
637 |
|
638 Create a Marshaller instance for each set of parameters, and use |
|
639 the "dumps" method to convert your data (represented as a tuple) |
|
640 to an XML-RPC params chunk. To write a fault response, pass a |
|
641 Fault instance instead. You may prefer to use the "dumps" module |
|
642 function for this purpose. |
|
643 """ |
|
644 |
|
645 # by the way, if you don't understand what's going on in here, |
|
646 # that's perfectly ok. |
|
647 |
|
648 def __init__(self, encoding=None, allow_none=0): |
|
649 self.memo = {} |
|
650 self.data = None |
|
651 self.encoding = encoding |
|
652 self.allow_none = allow_none |
|
653 |
|
654 dispatch = {} |
|
655 |
|
656 def dumps(self, values): |
|
657 out = [] |
|
658 write = out.append |
|
659 dump = self.__dump |
|
660 if isinstance(values, Fault): |
|
661 # fault instance |
|
662 write("<fault>\n") |
|
663 dump({'faultCode': values.faultCode, |
|
664 'faultString': values.faultString}, |
|
665 write) |
|
666 write("</fault>\n") |
|
667 else: |
|
668 # parameter block |
|
669 # FIXME: the xml-rpc specification allows us to leave out |
|
670 # the entire <params> block if there are no parameters. |
|
671 # however, changing this may break older code (including |
|
672 # old versions of xmlrpclib.py), so this is better left as |
|
673 # is for now. See @XMLRPC3 for more information. /F |
|
674 write("<params>\n") |
|
675 for v in values: |
|
676 write("<param>\n") |
|
677 dump(v, write) |
|
678 write("</param>\n") |
|
679 write("</params>\n") |
|
680 result = string.join(out, "") |
|
681 return result |
|
682 |
|
683 def __dump(self, value, write): |
|
684 try: |
|
685 f = self.dispatch[type(value)] |
|
686 except KeyError: |
|
687 # check if this object can be marshalled as a structure |
|
688 try: |
|
689 value.__dict__ |
|
690 except: |
|
691 raise TypeError, "cannot marshal %s objects" % type(value) |
|
692 # check if this class is a sub-class of a basic type, |
|
693 # because we don't know how to marshal these types |
|
694 # (e.g. a string sub-class) |
|
695 for type_ in type(value).__mro__: |
|
696 if type_ in self.dispatch.keys(): |
|
697 raise TypeError, "cannot marshal %s objects" % type(value) |
|
698 f = self.dispatch[InstanceType] |
|
699 f(self, value, write) |
|
700 |
|
701 def dump_nil (self, value, write): |
|
702 if not self.allow_none: |
|
703 raise TypeError, "cannot marshal None unless allow_none is enabled" |
|
704 write("<value><nil/></value>") |
|
705 dispatch[NoneType] = dump_nil |
|
706 |
|
707 def dump_int(self, value, write): |
|
708 # in case ints are > 32 bits |
|
709 if value > MAXINT or value < MININT: |
|
710 raise OverflowError, "int exceeds XML-RPC limits" |
|
711 write("<value><int>") |
|
712 write(str(value)) |
|
713 write("</int></value>\n") |
|
714 dispatch[IntType] = dump_int |
|
715 |
|
716 if _bool_is_builtin: |
|
717 def dump_bool(self, value, write): |
|
718 write("<value><boolean>") |
|
719 write(value and "1" or "0") |
|
720 write("</boolean></value>\n") |
|
721 dispatch[bool] = dump_bool |
|
722 |
|
723 def dump_long(self, value, write): |
|
724 if value > MAXINT or value < MININT: |
|
725 raise OverflowError, "long int exceeds XML-RPC limits" |
|
726 write("<value><int>") |
|
727 write(str(int(value))) |
|
728 write("</int></value>\n") |
|
729 dispatch[LongType] = dump_long |
|
730 |
|
731 def dump_double(self, value, write): |
|
732 write("<value><double>") |
|
733 write(repr(value)) |
|
734 write("</double></value>\n") |
|
735 dispatch[FloatType] = dump_double |
|
736 |
|
737 def dump_string(self, value, write, escape=escape): |
|
738 write("<value><string>") |
|
739 write(escape(value)) |
|
740 write("</string></value>\n") |
|
741 dispatch[StringType] = dump_string |
|
742 |
|
743 if unicode: |
|
744 def dump_unicode(self, value, write, escape=escape): |
|
745 value = value.encode(self.encoding) |
|
746 write("<value><string>") |
|
747 write(escape(value)) |
|
748 write("</string></value>\n") |
|
749 dispatch[UnicodeType] = dump_unicode |
|
750 |
|
751 def dump_array(self, value, write): |
|
752 i = id(value) |
|
753 if i in self.memo: |
|
754 raise TypeError, "cannot marshal recursive sequences" |
|
755 self.memo[i] = None |
|
756 dump = self.__dump |
|
757 write("<value><array><data>\n") |
|
758 for v in value: |
|
759 dump(v, write) |
|
760 write("</data></array></value>\n") |
|
761 del self.memo[i] |
|
762 dispatch[TupleType] = dump_array |
|
763 dispatch[ListType] = dump_array |
|
764 |
|
765 def dump_struct(self, value, write, escape=escape): |
|
766 i = id(value) |
|
767 if i in self.memo: |
|
768 raise TypeError, "cannot marshal recursive dictionaries" |
|
769 self.memo[i] = None |
|
770 dump = self.__dump |
|
771 write("<value><struct>\n") |
|
772 for k, v in value.items(): |
|
773 write("<member>\n") |
|
774 if type(k) is not StringType: |
|
775 if unicode and type(k) is UnicodeType: |
|
776 k = k.encode(self.encoding) |
|
777 else: |
|
778 raise TypeError, "dictionary key must be string" |
|
779 write("<name>%s</name>\n" % escape(k)) |
|
780 dump(v, write) |
|
781 write("</member>\n") |
|
782 write("</struct></value>\n") |
|
783 del self.memo[i] |
|
784 dispatch[DictType] = dump_struct |
|
785 |
|
786 if datetime: |
|
787 def dump_datetime(self, value, write): |
|
788 write("<value><dateTime.iso8601>") |
|
789 write(_strftime(value)) |
|
790 write("</dateTime.iso8601></value>\n") |
|
791 dispatch[datetime.datetime] = dump_datetime |
|
792 |
|
793 def dump_instance(self, value, write): |
|
794 # check for special wrappers |
|
795 if value.__class__ in WRAPPERS: |
|
796 self.write = write |
|
797 value.encode(self) |
|
798 del self.write |
|
799 else: |
|
800 # store instance attributes as a struct (really?) |
|
801 self.dump_struct(value.__dict__, write) |
|
802 dispatch[InstanceType] = dump_instance |
|
803 |
|
804 ## |
|
805 # XML-RPC unmarshaller. |
|
806 # |
|
807 # @see loads |
|
808 |
|
809 class Unmarshaller: |
|
810 """Unmarshal an XML-RPC response, based on incoming XML event |
|
811 messages (start, data, end). Call close() to get the resulting |
|
812 data structure. |
|
813 |
|
814 Note that this reader is fairly tolerant, and gladly accepts bogus |
|
815 XML-RPC data without complaining (but not bogus XML). |
|
816 """ |
|
817 |
|
818 # and again, if you don't understand what's going on in here, |
|
819 # that's perfectly ok. |
|
820 |
|
821 def __init__(self, use_datetime=0): |
|
822 self._type = None |
|
823 self._stack = [] |
|
824 self._marks = [] |
|
825 self._data = [] |
|
826 self._methodname = None |
|
827 self._encoding = "utf-8" |
|
828 self.append = self._stack.append |
|
829 self._use_datetime = use_datetime |
|
830 if use_datetime and not datetime: |
|
831 raise ValueError, "the datetime module is not available" |
|
832 |
|
833 def close(self): |
|
834 # return response tuple and target method |
|
835 if self._type is None or self._marks: |
|
836 raise ResponseError() |
|
837 if self._type == "fault": |
|
838 raise Fault(**self._stack[0]) |
|
839 return tuple(self._stack) |
|
840 |
|
841 def getmethodname(self): |
|
842 return self._methodname |
|
843 |
|
844 # |
|
845 # event handlers |
|
846 |
|
847 def xml(self, encoding, standalone): |
|
848 self._encoding = encoding |
|
849 # FIXME: assert standalone == 1 ??? |
|
850 |
|
851 def start(self, tag, attrs): |
|
852 # prepare to handle this element |
|
853 if tag == "array" or tag == "struct": |
|
854 self._marks.append(len(self._stack)) |
|
855 self._data = [] |
|
856 self._value = (tag == "value") |
|
857 |
|
858 def data(self, text): |
|
859 self._data.append(text) |
|
860 |
|
861 def end(self, tag, join=string.join): |
|
862 # call the appropriate end tag handler |
|
863 try: |
|
864 f = self.dispatch[tag] |
|
865 except KeyError: |
|
866 pass # unknown tag ? |
|
867 else: |
|
868 return f(self, join(self._data, "")) |
|
869 |
|
870 # |
|
871 # accelerator support |
|
872 |
|
873 def end_dispatch(self, tag, data): |
|
874 # dispatch data |
|
875 try: |
|
876 f = self.dispatch[tag] |
|
877 except KeyError: |
|
878 pass # unknown tag ? |
|
879 else: |
|
880 return f(self, data) |
|
881 |
|
882 # |
|
883 # element decoders |
|
884 |
|
885 dispatch = {} |
|
886 |
|
887 def end_nil (self, data): |
|
888 self.append(None) |
|
889 self._value = 0 |
|
890 dispatch["nil"] = end_nil |
|
891 |
|
892 def end_boolean(self, data): |
|
893 if data == "0": |
|
894 self.append(False) |
|
895 elif data == "1": |
|
896 self.append(True) |
|
897 else: |
|
898 raise TypeError, "bad boolean value" |
|
899 self._value = 0 |
|
900 dispatch["boolean"] = end_boolean |
|
901 |
|
902 def end_int(self, data): |
|
903 self.append(int(data)) |
|
904 self._value = 0 |
|
905 dispatch["i4"] = end_int |
|
906 dispatch["i8"] = end_int |
|
907 dispatch["int"] = end_int |
|
908 |
|
909 def end_double(self, data): |
|
910 self.append(float(data)) |
|
911 self._value = 0 |
|
912 dispatch["double"] = end_double |
|
913 |
|
914 def end_string(self, data): |
|
915 if self._encoding: |
|
916 data = _decode(data, self._encoding) |
|
917 self.append(_stringify(data)) |
|
918 self._value = 0 |
|
919 dispatch["string"] = end_string |
|
920 dispatch["name"] = end_string # struct keys are always strings |
|
921 |
|
922 def end_array(self, data): |
|
923 mark = self._marks.pop() |
|
924 # map arrays to Python lists |
|
925 self._stack[mark:] = [self._stack[mark:]] |
|
926 self._value = 0 |
|
927 dispatch["array"] = end_array |
|
928 |
|
929 def end_struct(self, data): |
|
930 mark = self._marks.pop() |
|
931 # map structs to Python dictionaries |
|
932 dict = {} |
|
933 items = self._stack[mark:] |
|
934 for i in range(0, len(items), 2): |
|
935 dict[_stringify(items[i])] = items[i+1] |
|
936 self._stack[mark:] = [dict] |
|
937 self._value = 0 |
|
938 dispatch["struct"] = end_struct |
|
939 |
|
940 def end_base64(self, data): |
|
941 value = Binary() |
|
942 value.decode(data) |
|
943 self.append(value) |
|
944 self._value = 0 |
|
945 dispatch["base64"] = end_base64 |
|
946 |
|
947 def end_dateTime(self, data): |
|
948 value = DateTime() |
|
949 value.decode(data) |
|
950 if self._use_datetime: |
|
951 value = _datetime_type(data) |
|
952 self.append(value) |
|
953 dispatch["dateTime.iso8601"] = end_dateTime |
|
954 |
|
955 def end_value(self, data): |
|
956 # if we stumble upon a value element with no internal |
|
957 # elements, treat it as a string element |
|
958 if self._value: |
|
959 self.end_string(data) |
|
960 dispatch["value"] = end_value |
|
961 |
|
962 def end_params(self, data): |
|
963 self._type = "params" |
|
964 dispatch["params"] = end_params |
|
965 |
|
966 def end_fault(self, data): |
|
967 self._type = "fault" |
|
968 dispatch["fault"] = end_fault |
|
969 |
|
970 def end_methodName(self, data): |
|
971 if self._encoding: |
|
972 data = _decode(data, self._encoding) |
|
973 self._methodname = data |
|
974 self._type = "methodName" # no params |
|
975 dispatch["methodName"] = end_methodName |
|
976 |
|
977 ## Multicall support |
|
978 # |
|
979 |
|
980 class _MultiCallMethod: |
|
981 # some lesser magic to store calls made to a MultiCall object |
|
982 # for batch execution |
|
983 def __init__(self, call_list, name): |
|
984 self.__call_list = call_list |
|
985 self.__name = name |
|
986 def __getattr__(self, name): |
|
987 return _MultiCallMethod(self.__call_list, "%s.%s" % (self.__name, name)) |
|
988 def __call__(self, *args): |
|
989 self.__call_list.append((self.__name, args)) |
|
990 |
|
991 class MultiCallIterator: |
|
992 """Iterates over the results of a multicall. Exceptions are |
|
993 thrown in response to xmlrpc faults.""" |
|
994 |
|
995 def __init__(self, results): |
|
996 self.results = results |
|
997 |
|
998 def __getitem__(self, i): |
|
999 item = self.results[i] |
|
1000 if type(item) == type({}): |
|
1001 raise Fault(item['faultCode'], item['faultString']) |
|
1002 elif type(item) == type([]): |
|
1003 return item[0] |
|
1004 else: |
|
1005 raise ValueError,\ |
|
1006 "unexpected type in multicall result" |
|
1007 |
|
1008 class MultiCall: |
|
1009 """server -> a object used to boxcar method calls |
|
1010 |
|
1011 server should be a ServerProxy object. |
|
1012 |
|
1013 Methods can be added to the MultiCall using normal |
|
1014 method call syntax e.g.: |
|
1015 |
|
1016 multicall = MultiCall(server_proxy) |
|
1017 multicall.add(2,3) |
|
1018 multicall.get_address("Guido") |
|
1019 |
|
1020 To execute the multicall, call the MultiCall object e.g.: |
|
1021 |
|
1022 add_result, address = multicall() |
|
1023 """ |
|
1024 |
|
1025 def __init__(self, server): |
|
1026 self.__server = server |
|
1027 self.__call_list = [] |
|
1028 |
|
1029 def __repr__(self): |
|
1030 return "<MultiCall at %x>" % id(self) |
|
1031 |
|
1032 __str__ = __repr__ |
|
1033 |
|
1034 def __getattr__(self, name): |
|
1035 return _MultiCallMethod(self.__call_list, name) |
|
1036 |
|
1037 def __call__(self): |
|
1038 marshalled_list = [] |
|
1039 for name, args in self.__call_list: |
|
1040 marshalled_list.append({'methodName' : name, 'params' : args}) |
|
1041 |
|
1042 return MultiCallIterator(self.__server.system.multicall(marshalled_list)) |
|
1043 |
|
1044 # -------------------------------------------------------------------- |
|
1045 # convenience functions |
|
1046 |
|
1047 ## |
|
1048 # Create a parser object, and connect it to an unmarshalling instance. |
|
1049 # This function picks the fastest available XML parser. |
|
1050 # |
|
1051 # return A (parser, unmarshaller) tuple. |
|
1052 |
|
1053 def getparser(use_datetime=0): |
|
1054 """getparser() -> parser, unmarshaller |
|
1055 |
|
1056 Create an instance of the fastest available parser, and attach it |
|
1057 to an unmarshalling object. Return both objects. |
|
1058 """ |
|
1059 if use_datetime and not datetime: |
|
1060 raise ValueError, "the datetime module is not available" |
|
1061 if FastParser and FastUnmarshaller: |
|
1062 if use_datetime: |
|
1063 mkdatetime = _datetime_type |
|
1064 else: |
|
1065 mkdatetime = _datetime |
|
1066 target = FastUnmarshaller(True, False, _binary, mkdatetime, Fault) |
|
1067 parser = FastParser(target) |
|
1068 else: |
|
1069 target = Unmarshaller(use_datetime=use_datetime) |
|
1070 if FastParser: |
|
1071 parser = FastParser(target) |
|
1072 elif SgmlopParser: |
|
1073 parser = SgmlopParser(target) |
|
1074 elif ExpatParser: |
|
1075 parser = ExpatParser(target) |
|
1076 else: |
|
1077 parser = SlowParser(target) |
|
1078 return parser, target |
|
1079 |
|
1080 ## |
|
1081 # Convert a Python tuple or a Fault instance to an XML-RPC packet. |
|
1082 # |
|
1083 # @def dumps(params, **options) |
|
1084 # @param params A tuple or Fault instance. |
|
1085 # @keyparam methodname If given, create a methodCall request for |
|
1086 # this method name. |
|
1087 # @keyparam methodresponse If given, create a methodResponse packet. |
|
1088 # If used with a tuple, the tuple must be a singleton (that is, |
|
1089 # it must contain exactly one element). |
|
1090 # @keyparam encoding The packet encoding. |
|
1091 # @return A string containing marshalled data. |
|
1092 |
|
1093 def dumps(params, methodname=None, methodresponse=None, encoding=None, |
|
1094 allow_none=0): |
|
1095 """data [,options] -> marshalled data |
|
1096 |
|
1097 Convert an argument tuple or a Fault instance to an XML-RPC |
|
1098 request (or response, if the methodresponse option is used). |
|
1099 |
|
1100 In addition to the data object, the following options can be given |
|
1101 as keyword arguments: |
|
1102 |
|
1103 methodname: the method name for a methodCall packet |
|
1104 |
|
1105 methodresponse: true to create a methodResponse packet. |
|
1106 If this option is used with a tuple, the tuple must be |
|
1107 a singleton (i.e. it can contain only one element). |
|
1108 |
|
1109 encoding: the packet encoding (default is UTF-8) |
|
1110 |
|
1111 All 8-bit strings in the data structure are assumed to use the |
|
1112 packet encoding. Unicode strings are automatically converted, |
|
1113 where necessary. |
|
1114 """ |
|
1115 |
|
1116 assert isinstance(params, TupleType) or isinstance(params, Fault),\ |
|
1117 "argument must be tuple or Fault instance" |
|
1118 |
|
1119 if isinstance(params, Fault): |
|
1120 methodresponse = 1 |
|
1121 elif methodresponse and isinstance(params, TupleType): |
|
1122 assert len(params) == 1, "response tuple must be a singleton" |
|
1123 |
|
1124 if not encoding: |
|
1125 encoding = "utf-8" |
|
1126 |
|
1127 if FastMarshaller: |
|
1128 m = FastMarshaller(encoding) |
|
1129 else: |
|
1130 m = Marshaller(encoding, allow_none) |
|
1131 |
|
1132 data = m.dumps(params) |
|
1133 |
|
1134 if encoding != "utf-8": |
|
1135 xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding) |
|
1136 else: |
|
1137 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default |
|
1138 |
|
1139 # standard XML-RPC wrappings |
|
1140 if methodname: |
|
1141 # a method call |
|
1142 if not isinstance(methodname, StringType): |
|
1143 methodname = methodname.encode(encoding) |
|
1144 data = ( |
|
1145 xmlheader, |
|
1146 "<methodCall>\n" |
|
1147 "<methodName>", methodname, "</methodName>\n", |
|
1148 data, |
|
1149 "</methodCall>\n" |
|
1150 ) |
|
1151 elif methodresponse: |
|
1152 # a method response, or a fault structure |
|
1153 data = ( |
|
1154 xmlheader, |
|
1155 "<methodResponse>\n", |
|
1156 data, |
|
1157 "</methodResponse>\n" |
|
1158 ) |
|
1159 else: |
|
1160 return data # return as is |
|
1161 return string.join(data, "") |
|
1162 |
|
1163 ## |
|
1164 # Convert an XML-RPC packet to a Python object. If the XML-RPC packet |
|
1165 # represents a fault condition, this function raises a Fault exception. |
|
1166 # |
|
1167 # @param data An XML-RPC packet, given as an 8-bit string. |
|
1168 # @return A tuple containing the unpacked data, and the method name |
|
1169 # (None if not present). |
|
1170 # @see Fault |
|
1171 |
|
1172 def loads(data, use_datetime=0): |
|
1173 """data -> unmarshalled data, method name |
|
1174 |
|
1175 Convert an XML-RPC packet to unmarshalled data plus a method |
|
1176 name (None if not present). |
|
1177 |
|
1178 If the XML-RPC packet represents a fault condition, this function |
|
1179 raises a Fault exception. |
|
1180 """ |
|
1181 p, u = getparser(use_datetime=use_datetime) |
|
1182 p.feed(data) |
|
1183 p.close() |
|
1184 return u.close(), u.getmethodname() |
|
1185 |
|
1186 |
|
1187 # -------------------------------------------------------------------- |
|
1188 # request dispatcher |
|
1189 |
|
1190 class _Method: |
|
1191 # some magic to bind an XML-RPC method to an RPC server. |
|
1192 # supports "nested" methods (e.g. examples.getStateName) |
|
1193 def __init__(self, send, name): |
|
1194 self.__send = send |
|
1195 self.__name = name |
|
1196 def __getattr__(self, name): |
|
1197 return _Method(self.__send, "%s.%s" % (self.__name, name)) |
|
1198 def __call__(self, *args): |
|
1199 return self.__send(self.__name, args) |
|
1200 |
|
1201 ## |
|
1202 # Standard transport class for XML-RPC over HTTP. |
|
1203 # <p> |
|
1204 # You can create custom transports by subclassing this method, and |
|
1205 # overriding selected methods. |
|
1206 |
|
1207 class Transport: |
|
1208 """Handles an HTTP transaction to an XML-RPC server.""" |
|
1209 |
|
1210 # client identifier (may be overridden) |
|
1211 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__ |
|
1212 |
|
1213 def __init__(self, use_datetime=0): |
|
1214 self._use_datetime = use_datetime |
|
1215 |
|
1216 ## |
|
1217 # Send a complete request, and parse the response. |
|
1218 # |
|
1219 # @param host Target host. |
|
1220 # @param handler Target PRC handler. |
|
1221 # @param request_body XML-RPC request body. |
|
1222 # @param verbose Debugging flag. |
|
1223 # @return Parsed response. |
|
1224 |
|
1225 def request(self, host, handler, request_body, verbose=0): |
|
1226 # issue XML-RPC request |
|
1227 |
|
1228 h = self.make_connection(host) |
|
1229 if verbose: |
|
1230 h.set_debuglevel(1) |
|
1231 |
|
1232 self.send_request(h, handler, request_body) |
|
1233 self.send_host(h, host) |
|
1234 self.send_user_agent(h) |
|
1235 self.send_content(h, request_body) |
|
1236 |
|
1237 errcode, errmsg, headers = h.getreply() |
|
1238 |
|
1239 if errcode != 200: |
|
1240 raise ProtocolError( |
|
1241 host + handler, |
|
1242 errcode, errmsg, |
|
1243 headers |
|
1244 ) |
|
1245 |
|
1246 self.verbose = verbose |
|
1247 |
|
1248 try: |
|
1249 sock = h._conn.sock |
|
1250 except AttributeError: |
|
1251 sock = None |
|
1252 |
|
1253 return self._parse_response(h.getfile(), sock) |
|
1254 |
|
1255 ## |
|
1256 # Create parser. |
|
1257 # |
|
1258 # @return A 2-tuple containing a parser and a unmarshaller. |
|
1259 |
|
1260 def getparser(self): |
|
1261 # get parser and unmarshaller |
|
1262 return getparser(use_datetime=self._use_datetime) |
|
1263 |
|
1264 ## |
|
1265 # Get authorization info from host parameter |
|
1266 # Host may be a string, or a (host, x509-dict) tuple; if a string, |
|
1267 # it is checked for a "user:pw@host" format, and a "Basic |
|
1268 # Authentication" header is added if appropriate. |
|
1269 # |
|
1270 # @param host Host descriptor (URL or (URL, x509 info) tuple). |
|
1271 # @return A 3-tuple containing (actual host, extra headers, |
|
1272 # x509 info). The header and x509 fields may be None. |
|
1273 |
|
1274 def get_host_info(self, host): |
|
1275 |
|
1276 x509 = {} |
|
1277 if isinstance(host, TupleType): |
|
1278 host, x509 = host |
|
1279 |
|
1280 import urllib |
|
1281 auth, host = urllib.splituser(host) |
|
1282 |
|
1283 if auth: |
|
1284 import base64 |
|
1285 auth = base64.encodestring(urllib.unquote(auth)) |
|
1286 auth = string.join(string.split(auth), "") # get rid of whitespace |
|
1287 extra_headers = [ |
|
1288 ("Authorization", "Basic " + auth) |
|
1289 ] |
|
1290 else: |
|
1291 extra_headers = None |
|
1292 |
|
1293 return host, extra_headers, x509 |
|
1294 |
|
1295 ## |
|
1296 # Connect to server. |
|
1297 # |
|
1298 # @param host Target host. |
|
1299 # @return A connection handle. |
|
1300 |
|
1301 def make_connection(self, host): |
|
1302 # create a HTTP connection object from a host descriptor |
|
1303 import httplib |
|
1304 host, extra_headers, x509 = self.get_host_info(host) |
|
1305 return httplib.HTTP(host) |
|
1306 |
|
1307 ## |
|
1308 # Send request header. |
|
1309 # |
|
1310 # @param connection Connection handle. |
|
1311 # @param handler Target RPC handler. |
|
1312 # @param request_body XML-RPC body. |
|
1313 |
|
1314 def send_request(self, connection, handler, request_body): |
|
1315 connection.putrequest("POST", handler) |
|
1316 |
|
1317 ## |
|
1318 # Send host name. |
|
1319 # |
|
1320 # @param connection Connection handle. |
|
1321 # @param host Host name. |
|
1322 |
|
1323 def send_host(self, connection, host): |
|
1324 host, extra_headers, x509 = self.get_host_info(host) |
|
1325 connection.putheader("Host", host) |
|
1326 if extra_headers: |
|
1327 if isinstance(extra_headers, DictType): |
|
1328 extra_headers = extra_headers.items() |
|
1329 for key, value in extra_headers: |
|
1330 connection.putheader(key, value) |
|
1331 |
|
1332 ## |
|
1333 # Send user-agent identifier. |
|
1334 # |
|
1335 # @param connection Connection handle. |
|
1336 |
|
1337 def send_user_agent(self, connection): |
|
1338 connection.putheader("User-Agent", self.user_agent) |
|
1339 |
|
1340 ## |
|
1341 # Send request body. |
|
1342 # |
|
1343 # @param connection Connection handle. |
|
1344 # @param request_body XML-RPC request body. |
|
1345 |
|
1346 def send_content(self, connection, request_body): |
|
1347 connection.putheader("Content-Type", "text/xml") |
|
1348 connection.putheader("Content-Length", str(len(request_body))) |
|
1349 connection.endheaders() |
|
1350 if request_body: |
|
1351 connection.send(request_body) |
|
1352 |
|
1353 ## |
|
1354 # Parse response. |
|
1355 # |
|
1356 # @param file Stream. |
|
1357 # @return Response tuple and target method. |
|
1358 |
|
1359 def parse_response(self, file): |
|
1360 # compatibility interface |
|
1361 return self._parse_response(file, None) |
|
1362 |
|
1363 ## |
|
1364 # Parse response (alternate interface). This is similar to the |
|
1365 # parse_response method, but also provides direct access to the |
|
1366 # underlying socket object (where available). |
|
1367 # |
|
1368 # @param file Stream. |
|
1369 # @param sock Socket handle (or None, if the socket object |
|
1370 # could not be accessed). |
|
1371 # @return Response tuple and target method. |
|
1372 |
|
1373 def _parse_response(self, file, sock): |
|
1374 # read response from input file/socket, and parse it |
|
1375 |
|
1376 p, u = self.getparser() |
|
1377 |
|
1378 while 1: |
|
1379 if sock: |
|
1380 response = sock.recv(1024) |
|
1381 else: |
|
1382 response = file.read(1024) |
|
1383 if not response: |
|
1384 break |
|
1385 if self.verbose: |
|
1386 print "body:", repr(response) |
|
1387 p.feed(response) |
|
1388 |
|
1389 file.close() |
|
1390 p.close() |
|
1391 |
|
1392 return u.close() |
|
1393 |
|
1394 ## |
|
1395 # Standard transport class for XML-RPC over HTTPS. |
|
1396 |
|
1397 class SafeTransport(Transport): |
|
1398 """Handles an HTTPS transaction to an XML-RPC server.""" |
|
1399 |
|
1400 # FIXME: mostly untested |
|
1401 |
|
1402 def make_connection(self, host): |
|
1403 # create a HTTPS connection object from a host descriptor |
|
1404 # host may be a string, or a (host, x509-dict) tuple |
|
1405 import httplib |
|
1406 host, extra_headers, x509 = self.get_host_info(host) |
|
1407 try: |
|
1408 HTTPS = httplib.HTTPS |
|
1409 except AttributeError: |
|
1410 raise NotImplementedError( |
|
1411 "your version of httplib doesn't support HTTPS" |
|
1412 ) |
|
1413 else: |
|
1414 return HTTPS(host, None, **(x509 or {})) |
|
1415 |
|
1416 ## |
|
1417 # Standard server proxy. This class establishes a virtual connection |
|
1418 # to an XML-RPC server. |
|
1419 # <p> |
|
1420 # This class is available as ServerProxy and Server. New code should |
|
1421 # use ServerProxy, to avoid confusion. |
|
1422 # |
|
1423 # @def ServerProxy(uri, **options) |
|
1424 # @param uri The connection point on the server. |
|
1425 # @keyparam transport A transport factory, compatible with the |
|
1426 # standard transport class. |
|
1427 # @keyparam encoding The default encoding used for 8-bit strings |
|
1428 # (default is UTF-8). |
|
1429 # @keyparam verbose Use a true value to enable debugging output. |
|
1430 # (printed to standard output). |
|
1431 # @see Transport |
|
1432 |
|
1433 class ServerProxy: |
|
1434 """uri [,options] -> a logical connection to an XML-RPC server |
|
1435 |
|
1436 uri is the connection point on the server, given as |
|
1437 scheme://host/target. |
|
1438 |
|
1439 The standard implementation always supports the "http" scheme. If |
|
1440 SSL socket support is available (Python 2.0), it also supports |
|
1441 "https". |
|
1442 |
|
1443 If the target part and the slash preceding it are both omitted, |
|
1444 "/RPC2" is assumed. |
|
1445 |
|
1446 The following options can be given as keyword arguments: |
|
1447 |
|
1448 transport: a transport factory |
|
1449 encoding: the request encoding (default is UTF-8) |
|
1450 |
|
1451 All 8-bit strings passed to the server proxy are assumed to use |
|
1452 the given encoding. |
|
1453 """ |
|
1454 |
|
1455 def __init__(self, uri, transport=None, encoding=None, verbose=0, |
|
1456 allow_none=0, use_datetime=0): |
|
1457 # establish a "logical" server connection |
|
1458 |
|
1459 # get the url |
|
1460 import urllib |
|
1461 type, uri = urllib.splittype(uri) |
|
1462 if type not in ("http", "https"): |
|
1463 raise IOError, "unsupported XML-RPC protocol" |
|
1464 self.__host, self.__handler = urllib.splithost(uri) |
|
1465 if not self.__handler: |
|
1466 self.__handler = "/RPC2" |
|
1467 |
|
1468 if transport is None: |
|
1469 if type == "https": |
|
1470 transport = SafeTransport(use_datetime=use_datetime) |
|
1471 else: |
|
1472 transport = Transport(use_datetime=use_datetime) |
|
1473 self.__transport = transport |
|
1474 |
|
1475 self.__encoding = encoding |
|
1476 self.__verbose = verbose |
|
1477 self.__allow_none = allow_none |
|
1478 |
|
1479 def __request(self, methodname, params): |
|
1480 # call a method on the remote server |
|
1481 |
|
1482 request = dumps(params, methodname, encoding=self.__encoding, |
|
1483 allow_none=self.__allow_none) |
|
1484 |
|
1485 response = self.__transport.request( |
|
1486 self.__host, |
|
1487 self.__handler, |
|
1488 request, |
|
1489 verbose=self.__verbose |
|
1490 ) |
|
1491 |
|
1492 if len(response) == 1: |
|
1493 response = response[0] |
|
1494 |
|
1495 return response |
|
1496 |
|
1497 def __repr__(self): |
|
1498 return ( |
|
1499 "<ServerProxy for %s%s>" % |
|
1500 (self.__host, self.__handler) |
|
1501 ) |
|
1502 |
|
1503 __str__ = __repr__ |
|
1504 |
|
1505 def __getattr__(self, name): |
|
1506 # magic method dispatcher |
|
1507 return _Method(self.__request, name) |
|
1508 |
|
1509 # note: to call a remote object with an non-standard name, use |
|
1510 # result getattr(server, "strange-python-name")(args) |
|
1511 |
|
1512 # compatibility |
|
1513 |
|
1514 Server = ServerProxy |
|
1515 |
|
1516 # -------------------------------------------------------------------- |
|
1517 # test code |
|
1518 |
|
1519 if __name__ == "__main__": |
|
1520 |
|
1521 # simple test program (from the XML-RPC specification) |
|
1522 |
|
1523 # server = ServerProxy("http://localhost:8000") # local server |
|
1524 server = ServerProxy("http://time.xmlrpc.com/RPC2") |
|
1525 |
|
1526 print server |
|
1527 |
|
1528 try: |
|
1529 print server.currentTime.getCurrentTime() |
|
1530 except Error, v: |
|
1531 print "ERROR", v |
|
1532 |
|
1533 multi = MultiCall(server) |
|
1534 multi.currentTime.getCurrentTime() |
|
1535 multi.currentTime.getCurrentTime() |
|
1536 try: |
|
1537 for response in multi(): |
|
1538 print response |
|
1539 except Error, v: |
|
1540 print "ERROR", v |