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