This commit was manufactured by cvs2svn to create tag 'r22a4-fork'.
[python/dscho.git] / Lib / xmlrpclib.py
blob9f96163be13eb99d5a38d959dfcce791bce893a9
2 # XML-RPC CLIENT LIBRARY
3 # $Id$
5 # an XML-RPC client interface for Python.
7 # the marshalling and response parser code can also be used to
8 # implement XML-RPC servers.
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.
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)
35 # Copyright (c) 1999-2001 by Secret Labs AB.
36 # Copyright (c) 1999-2001 by Fredrik Lundh.
38 # info@pythonware.com
39 # http://www.pythonware.com
41 # --------------------------------------------------------------------
42 # The XML-RPC client interface is
44 # Copyright (c) 1999-2001 by Secret Labs AB
45 # Copyright (c) 1999-2001 by Fredrik Lundh
47 # By obtaining, using, and/or copying this software and/or its
48 # associated documentation, you agree that you have read, understood,
49 # and will comply with the following terms and conditions:
51 # Permission to use, copy, modify, and distribute this software and
52 # its associated documentation for any purpose and without fee is
53 # hereby granted, provided that the above copyright notice appears in
54 # all copies, and that both that copyright notice and this permission
55 # notice appear in supporting documentation, and that the name of
56 # Secret Labs AB or the author not be used in advertising or publicity
57 # pertaining to distribution of the software without specific, written
58 # prior permission.
60 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
61 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
62 # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
63 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
64 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
65 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
66 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
67 # OF THIS SOFTWARE.
68 # --------------------------------------------------------------------
71 # things to look into before 1.0 final:
73 # TODO: unicode marshalling -DONE
74 # TODO: ascii-compatible encoding support -DONE
75 # TODO: safe transport -DONE (but mostly untested)
76 # TODO: sgmlop memory leak -DONE
77 # TODO: sgmlop xml parsing -DONE
78 # TODO: support unicode method names -DONE
79 # TODO: update selftest -DONE
80 # TODO: add docstrings -DONE
81 # TODO: clean up parser encoding (trust the parser) -DONE
82 # TODO: expat support -DONE
83 # TODO: _xmlrpclib accelerator support -DONE
84 # TODO: use smarter/faster escape from effdom
85 # TODO: support basic authentication (see robin's patch)
86 # TODO: fix host tuple handling in the server constructor
87 # TODO: let transport verify schemes
88 # TODO: update documentation
89 # TODO: authentication plugins
90 # TODO: memo problem (see HP's mail)
92 """
93 An XML-RPC client interface for Python.
95 The marshalling and response parser code can also be used to
96 implement XML-RPC servers.
98 Exported exceptions:
100 Error Base class for client errors
101 ProtocolError Indicates an HTTP protocol error
102 ResponseError Indicates a broken response package
103 Fault Indicates an XML-RPC fault package
105 Exported classes:
107 ServerProxy Represents a logical connection to an XML-RPC server
109 Boolean boolean wrapper to generate a "boolean" XML-RPC value
110 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
111 localtime integer value to generate a "dateTime.iso8601"
112 XML-RPC value
113 Binary binary data wrapper
115 SlowParser Slow but safe standard parser (based on xmllib)
116 Marshaller Generate an XML-RPC params chunk from a Python data structure
117 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
118 Transport Handles an HTTP transaction to an XML-RPC server
119 SafeTransport Handles an HTTPS transaction to an XML-RPC server
121 Exported constants:
123 True
124 False
126 Exported functions:
128 boolean Convert any Python value to an XML-RPC boolean
129 getparser Create instance of the fastest available parser & attach
130 to an unmarshalling object
131 dumps Convert an argument tuple or a Fault instance to an XML-RPC
132 request (or response, if the methodresponse option is used).
133 loads Convert an XML-RPC packet to unmarshalled data plus a method
134 name (None if not present).
137 import re, string, time, operator
138 from types import *
140 try:
141 unicode
142 except NameError:
143 unicode = None # unicode support not available
145 def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
146 # decode non-ascii string (if possible)
147 if unicode and encoding and is8bit(data):
148 data = unicode(data, encoding)
149 return data
151 if unicode:
152 def _stringify(string):
153 # convert to 7-bit ascii if possible
154 try:
155 return str(string)
156 except UnicodeError:
157 return string
158 else:
159 def _stringify(string):
160 return string
162 __version__ = "1.0b3"
164 # --------------------------------------------------------------------
165 # Exceptions
167 class Error(Exception):
168 """Base class for client errors."""
169 def __str__(self):
170 return repr(self)
172 class ProtocolError(Error):
173 """Indicates an HTTP protocol error."""
174 def __init__(self, url, errcode, errmsg, headers):
175 Error.__init__(self)
176 self.url = url
177 self.errcode = errcode
178 self.errmsg = errmsg
179 self.headers = headers
180 def __repr__(self):
181 return (
182 "<ProtocolError for %s: %s %s>" %
183 (self.url, self.errcode, self.errmsg)
186 class ResponseError(Error):
187 """Indicates a broken response package."""
188 pass
190 class Fault(Error):
191 """Indicates an XML-RPC fault package."""
192 def __init__(self, faultCode, faultString, **extra):
193 Error.__init__(self)
194 self.faultCode = faultCode
195 self.faultString = faultString
196 def __repr__(self):
197 return (
198 "<Fault %s: %s>" %
199 (self.faultCode, repr(self.faultString))
202 # --------------------------------------------------------------------
203 # Special values
205 class Boolean:
206 """Boolean-value wrapper.
208 Use True or False to generate a "boolean" XML-RPC value.
211 def __init__(self, value = 0):
212 self.value = operator.truth(value)
214 def encode(self, out):
215 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
217 def __cmp__(self, other):
218 if isinstance(other, Boolean):
219 other = other.value
220 return cmp(self.value, other)
222 def __repr__(self):
223 if self.value:
224 return "<Boolean True at %x>" % id(self)
225 else:
226 return "<Boolean False at %x>" % id(self)
228 def __int__(self):
229 return self.value
231 def __nonzero__(self):
232 return self.value
234 True, False = Boolean(1), Boolean(0)
236 def boolean(value, truefalse=(False, True)):
237 """Convert any Python value to XML-RPC 'boolean'."""
238 return truefalse[operator.truth(value)]
240 class DateTime:
241 """DateTime wrapper for an ISO 8601 string or time tuple or
242 localtime integer value to generate 'dateTime.iso8601' XML-RPC
243 value.
246 def __init__(self, value=0):
247 if not isinstance(value, StringType):
248 if not isinstance(value, TupleType):
249 if value == 0:
250 value = time.time()
251 value = time.localtime(value)
252 value = time.strftime("%Y%m%dT%H:%M:%S", value)
253 self.value = value
255 def __cmp__(self, other):
256 if isinstance(other, DateTime):
257 other = other.value
258 return cmp(self.value, other)
260 def __repr__(self):
261 return "<DateTime %s at %x>" % (self.value, id(self))
263 def decode(self, data):
264 self.value = string.strip(data)
266 def encode(self, out):
267 out.write("<value><dateTime.iso8601>")
268 out.write(self.value)
269 out.write("</dateTime.iso8601></value>\n")
271 def datetime(data):
272 value = DateTime()
273 value.decode(data)
274 return value
276 class Binary:
277 """Wrapper for binary data."""
279 def __init__(self, data=None):
280 self.data = data
282 def __cmp__(self, other):
283 if isinstance(other, Binary):
284 other = other.data
285 return cmp(self.data, other)
287 def decode(self, data):
288 import base64
289 self.data = base64.decodestring(data)
291 def encode(self, out):
292 import base64, StringIO
293 out.write("<value><base64>\n")
294 base64.encode(StringIO.StringIO(self.data), out)
295 out.write("</base64></value>\n")
297 def binary(data):
298 value = Binary()
299 value.decode(data)
300 return value
302 WRAPPERS = DateTime, Binary, Boolean
304 # --------------------------------------------------------------------
305 # XML parsers
307 try:
308 # optional xmlrpclib accelerator. for more information on this
309 # component, contact info@pythonware.com
310 import _xmlrpclib
311 FastParser = _xmlrpclib.Parser
312 FastUnmarshaller = _xmlrpclib.Unmarshaller
313 except (AttributeError, ImportError):
314 FastParser = FastUnmarshaller = None
317 # the SGMLOP parser is about 15x faster than Python's builtin
318 # XML parser. SGMLOP sources can be downloaded from:
320 # http://www.pythonware.com/products/xml/sgmlop.htm
323 try:
324 import sgmlop
325 if not hasattr(sgmlop, "XMLParser"):
326 raise ImportError
327 except ImportError:
328 SgmlopParser = None # sgmlop accelerator not available
329 else:
330 class SgmlopParser:
331 def __init__(self, target):
333 # setup callbacks
334 self.finish_starttag = target.start
335 self.finish_endtag = target.end
336 self.handle_data = target.data
337 self.handle_xml = target.xml
339 # activate parser
340 self.parser = sgmlop.XMLParser()
341 self.parser.register(self)
342 self.feed = self.parser.feed
343 self.entity = {
344 "amp": "&", "gt": ">", "lt": "<",
345 "apos": "'", "quot": '"'
348 def close(self):
349 try:
350 self.parser.close()
351 finally:
352 self.parser = self.feed = None # nuke circular reference
354 def handle_proc(self, tag, attr):
355 import re
356 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
357 if m:
358 self.handle_xml(m.group(1), 1)
360 def handle_entityref(self, entity):
361 # <string> entity
362 try:
363 self.handle_data(self.entity[entity])
364 except KeyError:
365 self.handle_data("&%s;" % entity)
367 try:
368 from xml.parsers import expat
369 except ImportError:
370 ExpatParser = None
371 else:
372 class ExpatParser:
373 # fast expat parser for Python 2.0. this is about 50%
374 # slower than sgmlop, on roundtrip testing
375 def __init__(self, target):
376 self._parser = parser = expat.ParserCreate(None, None)
377 self._target = target
378 parser.StartElementHandler = target.start
379 parser.EndElementHandler = target.end
380 parser.CharacterDataHandler = target.data
381 encoding = None
382 if not parser.returns_unicode:
383 encoding = "utf-8"
384 target.xml(encoding, None)
386 def feed(self, data):
387 self._parser.Parse(data, 0)
389 def close(self):
390 self._parser.Parse("", 1) # end of data
391 del self._target, self._parser # get rid of circular references
393 class SlowParser:
394 """Default XML parser (based on xmllib.XMLParser)."""
395 # this is about 10 times slower than sgmlop, on roundtrip
396 # testing.
397 def __init__(self, target):
398 import xmllib # lazy subclassing (!)
399 if xmllib.XMLParser not in SlowParser.__bases__:
400 SlowParser.__bases__ = (xmllib.XMLParser,)
401 self.handle_xml = target.xml
402 self.unknown_starttag = target.start
403 self.handle_data = target.data
404 self.unknown_endtag = target.end
405 try:
406 xmllib.XMLParser.__init__(self, accept_utf8=1)
407 except TypeError:
408 xmllib.XMLParser.__init__(self) # pre-2.0
410 # --------------------------------------------------------------------
411 # XML-RPC marshalling and unmarshalling code
413 class Marshaller:
414 """Generate an XML-RPC params chunk from a Python data structure.
416 Create a Marshaller instance for each set of parameters, and use
417 the "dumps" method to convert your data (represented as a tuple)
418 to an XML-RPC params chunk. To write a fault response, pass a
419 Fault instance instead. You may prefer to use the "dumps" module
420 function for this purpose.
423 # by the way, if you don't understand what's going on in here,
424 # that's perfectly ok.
426 def __init__(self, encoding=None):
427 self.memo = {}
428 self.data = None
429 self.encoding = encoding
431 dispatch = {}
433 def dumps(self, values):
434 self.__out = []
435 self.write = write = self.__out.append
436 if isinstance(values, Fault):
437 # fault instance
438 write("<fault>\n")
439 self.__dump(vars(values))
440 write("</fault>\n")
441 else:
442 # parameter block
443 # FIXME: the xml-rpc specification allows us to leave out
444 # the entire <params> block if there are no parameters.
445 # however, changing this may break older code (including
446 # old versions of xmlrpclib.py), so this is better left as
447 # is for now. See @XMLRPC3 for more information. /F
448 write("<params>\n")
449 for v in values:
450 write("<param>\n")
451 self.__dump(v)
452 write("</param>\n")
453 write("</params>\n")
454 result = string.join(self.__out, "")
455 del self.__out, self.write # don't need this any more
456 return result
458 def __dump(self, value):
459 try:
460 f = self.dispatch[type(value)]
461 except KeyError:
462 raise TypeError, "cannot marshal %s objects" % type(value)
463 else:
464 f(self, value)
466 def dump_int(self, value):
467 self.write("<value><int>%s</int></value>\n" % value)
468 dispatch[IntType] = dump_int
470 def dump_double(self, value):
471 self.write("<value><double>%s</double></value>\n" % value)
472 dispatch[FloatType] = dump_double
474 def dump_string(self, value):
475 from cgi import escape
476 self.write("<value><string>%s</string></value>\n" % escape(value))
477 dispatch[StringType] = dump_string
479 if unicode:
480 def dump_unicode(self, value):
481 value = value.encode(self.encoding)
482 from cgi import escape
483 self.write("<value><string>%s</string></value>\n" % escape(value))
484 dispatch[UnicodeType] = dump_unicode
486 def container(self, value):
487 if value:
488 i = id(value)
489 if self.memo.has_key(i):
490 raise TypeError, "cannot marshal recursive data structures"
491 self.memo[i] = None
493 def dump_array(self, value):
494 self.container(value)
495 write = self.write
496 write("<value><array><data>\n")
497 for v in value:
498 self.__dump(v)
499 write("</data></array></value>\n")
500 dispatch[TupleType] = dump_array
501 dispatch[ListType] = dump_array
503 def dump_struct(self, value):
504 self.container(value)
505 write = self.write
506 write("<value><struct>\n")
507 for k, v in value.items():
508 write("<member>\n")
509 if type(k) is not StringType:
510 raise TypeError, "dictionary key must be string"
511 from cgi import escape
512 write("<name>%s</name>\n" % escape(k))
513 self.__dump(v)
514 write("</member>\n")
515 write("</struct></value>\n")
516 dispatch[DictType] = dump_struct
518 def dump_instance(self, value):
519 # check for special wrappers
520 if value.__class__ in WRAPPERS:
521 value.encode(self)
522 else:
523 # store instance attributes as a struct (really?)
524 self.dump_struct(value.__dict__)
525 dispatch[InstanceType] = dump_instance
527 class Unmarshaller:
528 """Unmarshal an XML-RPC response, based on incoming XML event
529 messages (start, data, end). Call close() to get the resulting
530 data structure.
532 Note that this reader is fairly tolerant, and gladly accepts bogus
533 XML-RPC data without complaining (but not bogus XML).
536 # and again, if you don't understand what's going on in here,
537 # that's perfectly ok.
539 def __init__(self):
540 self._type = None
541 self._stack = []
542 self._marks = []
543 self._data = []
544 self._methodname = None
545 self._encoding = "utf-8"
546 self.append = self._stack.append
548 def close(self):
549 # return response tuple and target method
550 if self._type is None or self._marks:
551 raise ResponseError()
552 if self._type == "fault":
553 raise apply(Fault, (), self._stack[0])
554 return tuple(self._stack)
556 def getmethodname(self):
557 return self._methodname
560 # event handlers
562 def xml(self, encoding, standalone):
563 self._encoding = encoding
564 # FIXME: assert standalone == 1 ???
566 def start(self, tag, attrs):
567 # prepare to handle this element
568 if tag == "array" or tag == "struct":
569 self._marks.append(len(self._stack))
570 self._data = []
571 self._value = (tag == "value")
573 def data(self, text):
574 self._data.append(text)
576 def end(self, tag):
577 # call the appropriate end tag handler
578 try:
579 f = self.dispatch[tag]
580 except KeyError:
581 pass # unknown tag ?
582 else:
583 return f(self, self._data)
586 # accelerator support
588 def end_dispatch(self, tag, data):
589 # dispatch data
590 try:
591 f = self.dispatch[tag]
592 except KeyError:
593 pass # unknown tag ?
594 else:
595 return f(self, data)
598 # element decoders
600 dispatch = {}
602 def end_boolean(self, data, join=string.join):
603 data = join(data, "")
604 if data == "0":
605 self.append(False)
606 elif data == "1":
607 self.append(True)
608 else:
609 raise TypeError, "bad boolean value"
610 self._value = 0
611 dispatch["boolean"] = end_boolean
613 def end_int(self, data, join=string.join):
614 self.append(int(join(data, "")))
615 self._value = 0
616 dispatch["i4"] = end_int
617 dispatch["int"] = end_int
619 def end_double(self, data, join=string.join):
620 self.append(float(join(data, "")))
621 self._value = 0
622 dispatch["double"] = end_double
624 def end_string(self, data, join=string.join):
625 data = join(data, "")
626 if self._encoding:
627 data = _decode(data, self._encoding)
628 self.append(_stringify(data))
629 self._value = 0
630 dispatch["string"] = end_string
631 dispatch["name"] = end_string # struct keys are always strings
633 def end_array(self, data):
634 mark = self._marks[-1]
635 del self._marks[-1]
636 # map arrays to Python lists
637 self._stack[mark:] = [self._stack[mark:]]
638 self._value = 0
639 dispatch["array"] = end_array
641 def end_struct(self, data):
642 mark = self._marks[-1]
643 del self._marks[-1]
644 # map structs to Python dictionaries
645 dict = {}
646 items = self._stack[mark:]
647 for i in range(0, len(items), 2):
648 dict[_stringify(items[i])] = items[i+1]
649 self._stack[mark:] = [dict]
650 self._value = 0
651 dispatch["struct"] = end_struct
653 def end_base64(self, data, join=string.join):
654 value = Binary()
655 value.decode(join(data, ""))
656 self.append(value)
657 self._value = 0
658 dispatch["base64"] = end_base64
660 def end_dateTime(self, data, join=string.join):
661 value = DateTime()
662 value.decode(join(data, ""))
663 self.append(value)
664 dispatch["dateTime.iso8601"] = end_dateTime
666 def end_value(self, data):
667 # if we stumble upon an value element with no internal
668 # elements, treat it as a string element
669 if self._value:
670 self.end_string(data)
671 dispatch["value"] = end_value
673 def end_params(self, data):
674 self._type = "params"
675 dispatch["params"] = end_params
677 def end_fault(self, data):
678 self._type = "fault"
679 dispatch["fault"] = end_fault
681 def end_methodName(self, data, join=string.join):
682 data = join(data, "")
683 if self._encoding:
684 data = _decode(data, self._encoding)
685 self._methodname = data
686 self._type = "methodName" # no params
687 dispatch["methodName"] = end_methodName
690 # --------------------------------------------------------------------
691 # convenience functions
693 def getparser():
694 """getparser() -> parser, unmarshaller
696 Create an instance of the fastest available parser, and attach it
697 to an unmarshalling object. Return both objects.
699 if FastParser and FastUnmarshaller:
700 target = FastUnmarshaller(True, False, binary, datetime)
701 parser = FastParser(target)
702 else:
703 target = Unmarshaller()
704 if FastParser:
705 parser = FastParser(target)
706 elif SgmlopParser:
707 parser = SgmlopParser(target)
708 elif ExpatParser:
709 parser = ExpatParser(target)
710 else:
711 parser = SlowParser(target)
712 return parser, target
714 def dumps(params, methodname=None, methodresponse=None, encoding=None):
715 """data [,options] -> marshalled data
717 Convert an argument tuple or a Fault instance to an XML-RPC
718 request (or response, if the methodresponse option is used).
720 In addition to the data object, the following options can be given
721 as keyword arguments:
723 methodname: the method name for a methodCall packet
725 methodresponse: true to create a methodResponse packet.
726 If this option is used with a tuple, the tuple must be
727 a singleton (i.e. it can contain only one element).
729 encoding: the packet encoding (default is UTF-8)
731 All 8-bit strings in the data structure are assumed to use the
732 packet encoding. Unicode strings are automatically converted,
733 where necessary.
736 assert isinstance(params, TupleType) or isinstance(params, Fault),\
737 "argument must be tuple or Fault instance"
739 if isinstance(params, Fault):
740 methodresponse = 1
741 elif methodresponse and isinstance(params, TupleType):
742 assert len(params) == 1, "response tuple must be a singleton"
744 if not encoding:
745 encoding = "utf-8"
747 m = Marshaller(encoding)
748 data = m.dumps(params)
750 if encoding != "utf-8":
751 xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
752 else:
753 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
755 # standard XML-RPC wrappings
756 if methodname:
757 # a method call
758 if not isinstance(methodname, StringType):
759 methodname = methodname.encode(encoding)
760 data = (
761 xmlheader,
762 "<methodCall>\n"
763 "<methodName>", methodname, "</methodName>\n",
764 data,
765 "</methodCall>\n"
767 elif methodresponse:
768 # a method response, or a fault structure
769 data = (
770 xmlheader,
771 "<methodResponse>\n",
772 data,
773 "</methodResponse>\n"
775 else:
776 return data # return as is
777 return string.join(data, "")
779 def loads(data):
780 """data -> unmarshalled data, method name
782 Convert an XML-RPC packet to unmarshalled data plus a method
783 name (None if not present).
785 If the XML-RPC packet represents a fault condition, this function
786 raises a Fault exception.
788 p, u = getparser()
789 p.feed(data)
790 p.close()
791 return u.close(), u.getmethodname()
794 # --------------------------------------------------------------------
795 # request dispatcher
797 class _Method:
798 # some magic to bind an XML-RPC method to an RPC server.
799 # supports "nested" methods (e.g. examples.getStateName)
800 def __init__(self, send, name):
801 self.__send = send
802 self.__name = name
803 def __getattr__(self, name):
804 return _Method(self.__send, "%s.%s" % (self.__name, name))
805 def __call__(self, *args):
806 return self.__send(self.__name, args)
809 class Transport:
810 """Handles an HTTP transaction to an XML-RPC server."""
812 # client identifier (may be overridden)
813 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
815 def request(self, host, handler, request_body, verbose=0):
816 # issue XML-RPC request
818 h = self.make_connection(host)
819 if verbose:
820 h.set_debuglevel(1)
822 self.send_request(h, handler, request_body)
823 self.send_host(h, host)
824 self.send_user_agent(h)
825 self.send_content(h, request_body)
827 errcode, errmsg, headers = h.getreply()
829 if errcode != 200:
830 raise ProtocolError(
831 host + handler,
832 errcode, errmsg,
833 headers
836 self.verbose = verbose
838 return self.parse_response(h.getfile())
840 def getparser(self):
841 # get parser and unmarshaller
842 return getparser()
844 def make_connection(self, host):
845 # create a HTTP connection object from a host descriptor
846 import httplib
847 return httplib.HTTP(host)
849 def send_request(self, connection, handler, request_body):
850 connection.putrequest("POST", handler)
852 def send_host(self, connection, host):
853 connection.putheader("Host", host)
855 def send_user_agent(self, connection):
856 connection.putheader("User-Agent", self.user_agent)
858 def send_content(self, connection, request_body):
859 connection.putheader("Content-Type", "text/xml")
860 connection.putheader("Content-Length", str(len(request_body)))
861 connection.endheaders()
862 if request_body:
863 connection.send(request_body)
865 def parse_response(self, f):
866 # read response from input file, and parse it
868 p, u = self.getparser()
870 while 1:
871 response = f.read(1024)
872 if not response:
873 break
874 if self.verbose:
875 print "body:", repr(response)
876 p.feed(response)
878 f.close()
879 p.close()
881 return u.close()
883 class SafeTransport(Transport):
884 """Handles an HTTPS transaction to an XML-RPC server."""
886 def make_connection(self, host):
887 # create a HTTPS connection object from a host descriptor
888 # host may be a string, or a (host, x509-dict) tuple
889 import httplib
890 if isinstance(host, TupleType):
891 host, x509 = host
892 else:
893 x509 = {}
894 try:
895 HTTPS = httplib.HTTPS
896 except AttributeError:
897 raise NotImplementedError,\
898 "your version of httplib doesn't support HTTPS"
899 else:
900 return apply(HTTPS, (host, None), x509)
902 def send_host(self, connection, host):
903 if isinstance(host, TupleType):
904 host, x509 = host
905 connection.putheader("Host", host)
907 class ServerProxy:
908 """uri [,options] -> a logical connection to an XML-RPC server
910 uri is the connection point on the server, given as
911 scheme://host/target.
913 The standard implementation always supports the "http" scheme. If
914 SSL socket support is available (Python 2.0), it also supports
915 "https".
917 If the target part and the slash preceding it are both omitted,
918 "/RPC2" is assumed.
920 The following options can be given as keyword arguments:
922 transport: a transport factory
923 encoding: the request encoding (default is UTF-8)
925 All 8-bit strings passed to the server proxy are assumed to use
926 the given encoding.
929 def __init__(self, uri, transport=None, encoding=None, verbose=0):
930 # establish a "logical" server connection
932 # get the url
933 import urllib
934 type, uri = urllib.splittype(uri)
935 if type not in ("http", "https"):
936 raise IOError, "unsupported XML-RPC protocol"
937 self.__host, self.__handler = urllib.splithost(uri)
938 if not self.__handler:
939 self.__handler = "/RPC2"
941 if transport is None:
942 if type == "https":
943 transport = SafeTransport()
944 else:
945 transport = Transport()
946 self.__transport = transport
948 self.__encoding = encoding
949 self.__verbose = verbose
951 def __request(self, methodname, params):
952 # call a method on the remote server
954 request = dumps(params, methodname, encoding=self.__encoding)
956 response = self.__transport.request(
957 self.__host,
958 self.__handler,
959 request,
960 verbose=self.__verbose
963 if len(response) == 1:
964 response = response[0]
966 return response
968 def __repr__(self):
969 return (
970 "<ServerProxy for %s%s>" %
971 (self.__host, self.__handler)
974 __str__ = __repr__
976 def __getattr__(self, name):
977 # magic method dispatcher
978 return _Method(self.__request, name)
980 # note: to call a remote object with an non-standard name, use
981 # result getattr(server, "strange-python-name")(args)
983 # compatibility
984 Server = ServerProxy
986 # --------------------------------------------------------------------
987 # test code
989 if __name__ == "__main__":
991 # simple test program (from the XML-RPC specification)
993 # server = ServerProxy("http://localhost:8000") # local server
994 server = ServerProxy("http://betty.userland.com")
996 print server
998 try:
999 print server.examples.getStateName(41)
1000 except Error, v:
1001 print "ERROR", v