This commit was manufactured by cvs2svn to create tag 'r221c2'.
[python/dscho.git] / Lib / xmlrpclib.py
blob78bdd6ff157eaf831a379e43b07418f5f04b9fde
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)
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-10 sm Allow long ints to be passed as ints if they don't overflow
37 # 2001-10-17 sm test for int and long overflow (allows use on 64-bit systems)
38 # 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix)
40 # Copyright (c) 1999-2001 by Secret Labs AB.
41 # Copyright (c) 1999-2001 by Fredrik Lundh.
43 # info@pythonware.com
44 # http://www.pythonware.com
46 # --------------------------------------------------------------------
47 # The XML-RPC client interface is
49 # Copyright (c) 1999-2001 by Secret Labs AB
50 # Copyright (c) 1999-2001 by Fredrik Lundh
52 # By obtaining, using, and/or copying this software and/or its
53 # associated documentation, you agree that you have read, understood,
54 # and will comply with the following terms and conditions:
56 # Permission to use, copy, modify, and distribute this software and
57 # its associated documentation for any purpose and without fee is
58 # hereby granted, provided that the above copyright notice appears in
59 # all copies, and that both that copyright notice and this permission
60 # notice appear in supporting documentation, and that the name of
61 # Secret Labs AB or the author not be used in advertising or publicity
62 # pertaining to distribution of the software without specific, written
63 # prior permission.
65 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
66 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
67 # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
68 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
69 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
70 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
71 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
72 # OF THIS SOFTWARE.
73 # --------------------------------------------------------------------
76 # things to look into:
78 # TODO: support basic authentication (see robin's patch)
79 # TODO: fix host tuple handling in the server constructor
80 # TODO: let transport verify schemes
81 # TODO: update documentation
82 # TODO: authentication plugins
84 """
85 An XML-RPC client interface for Python.
87 The marshalling and response parser code can also be used to
88 implement XML-RPC servers.
90 Exported exceptions:
92 Error Base class for client errors
93 ProtocolError Indicates an HTTP protocol error
94 ResponseError Indicates a broken response package
95 Fault Indicates an XML-RPC fault package
97 Exported classes:
99 ServerProxy Represents a logical connection to an XML-RPC server
101 Boolean boolean wrapper to generate a "boolean" XML-RPC value
102 DateTime dateTime wrapper for an ISO 8601 string or time tuple or
103 localtime integer value to generate a "dateTime.iso8601"
104 XML-RPC value
105 Binary binary data wrapper
107 SlowParser Slow but safe standard parser (based on xmllib)
108 Marshaller Generate an XML-RPC params chunk from a Python data structure
109 Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
110 Transport Handles an HTTP transaction to an XML-RPC server
111 SafeTransport Handles an HTTPS transaction to an XML-RPC server
113 Exported constants:
115 True
116 False
118 Exported functions:
120 boolean Convert any Python value to an XML-RPC boolean
121 getparser Create instance of the fastest available parser & attach
122 to an unmarshalling object
123 dumps Convert an argument tuple or a Fault instance to an XML-RPC
124 request (or response, if the methodresponse option is used).
125 loads Convert an XML-RPC packet to unmarshalled data plus a method
126 name (None if not present).
129 import re, string, time, operator
131 from types import *
133 try:
134 unicode
135 except NameError:
136 unicode = None # unicode support not available
138 def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
139 # decode non-ascii string (if possible)
140 if unicode and encoding and is8bit(data):
141 data = unicode(data, encoding)
142 return data
144 def escape(s, replace=string.replace):
145 s = replace(s, "&", "&")
146 s = replace(s, "<", "&lt;")
147 return replace(s, ">", "&gt;",)
149 MAXINT = 2L**31-1
150 MININT = -2L**31
152 if unicode:
153 def _stringify(string):
154 # convert to 7-bit ascii if possible
155 try:
156 return str(string)
157 except UnicodeError:
158 return string
159 else:
160 def _stringify(string):
161 return string
163 __version__ = "1.0.0"
165 # --------------------------------------------------------------------
166 # Exceptions
168 class Error(Exception):
169 """Base class for client errors."""
170 def __str__(self):
171 return repr(self)
173 class ProtocolError(Error):
174 """Indicates an HTTP protocol error."""
175 def __init__(self, url, errcode, errmsg, headers):
176 Error.__init__(self)
177 self.url = url
178 self.errcode = errcode
179 self.errmsg = errmsg
180 self.headers = headers
181 def __repr__(self):
182 return (
183 "<ProtocolError for %s: %s %s>" %
184 (self.url, self.errcode, self.errmsg)
187 class ResponseError(Error):
188 """Indicates a broken response package."""
189 pass
191 class Fault(Error):
192 """Indicates an XML-RPC fault package."""
193 def __init__(self, faultCode, faultString, **extra):
194 Error.__init__(self)
195 self.faultCode = faultCode
196 self.faultString = faultString
197 def __repr__(self):
198 return (
199 "<Fault %s: %s>" %
200 (self.faultCode, repr(self.faultString))
203 # --------------------------------------------------------------------
204 # Special values
206 class Boolean:
207 """Boolean-value wrapper.
209 Use True or False to generate a "boolean" XML-RPC value.
212 def __init__(self, value = 0):
213 self.value = operator.truth(value)
215 def encode(self, out):
216 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
218 def __cmp__(self, other):
219 if isinstance(other, Boolean):
220 other = other.value
221 return cmp(self.value, other)
223 def __repr__(self):
224 if self.value:
225 return "<Boolean True at %x>" % id(self)
226 else:
227 return "<Boolean False at %x>" % id(self)
229 def __int__(self):
230 return self.value
232 def __nonzero__(self):
233 return self.value
235 True, False = Boolean(1), Boolean(0)
237 def boolean(value, truefalse=(False, True)):
238 """Convert any Python value to XML-RPC 'boolean'."""
239 return truefalse[operator.truth(value)]
241 class DateTime:
242 """DateTime wrapper for an ISO 8601 string or time tuple or
243 localtime integer value to generate 'dateTime.iso8601' XML-RPC
244 value.
247 def __init__(self, value=0):
248 if not isinstance(value, StringType):
249 if not isinstance(value, TupleType):
250 if value == 0:
251 value = time.time()
252 value = time.localtime(value)
253 value = time.strftime("%Y%m%dT%H:%M:%S", value)
254 self.value = value
256 def __cmp__(self, other):
257 if isinstance(other, DateTime):
258 other = other.value
259 return cmp(self.value, other)
261 def __repr__(self):
262 return "<DateTime %s at %x>" % (self.value, id(self))
264 def decode(self, data):
265 self.value = string.strip(data)
267 def encode(self, out):
268 out.write("<value><dateTime.iso8601>")
269 out.write(self.value)
270 out.write("</dateTime.iso8601></value>\n")
272 def datetime(data):
273 value = DateTime()
274 value.decode(data)
275 return value
277 class Binary:
278 """Wrapper for binary data."""
280 def __init__(self, data=None):
281 self.data = data
283 def __cmp__(self, other):
284 if isinstance(other, Binary):
285 other = other.data
286 return cmp(self.data, other)
288 def decode(self, data):
289 import base64
290 self.data = base64.decodestring(data)
292 def encode(self, out):
293 import base64, StringIO
294 out.write("<value><base64>\n")
295 base64.encode(StringIO.StringIO(self.data), out)
296 out.write("</base64></value>\n")
298 def binary(data):
299 value = Binary()
300 value.decode(data)
301 return value
303 WRAPPERS = DateTime, Binary, Boolean
305 # --------------------------------------------------------------------
306 # XML parsers
308 try:
309 # optional xmlrpclib accelerator. for more information on this
310 # component, contact info@pythonware.com
311 import _xmlrpclib
312 FastParser = _xmlrpclib.Parser
313 FastUnmarshaller = _xmlrpclib.Unmarshaller
314 except (AttributeError, ImportError):
315 FastParser = FastUnmarshaller = None
318 # the SGMLOP parser is about 15x faster than Python's builtin
319 # XML parser. SGMLOP sources can be downloaded from:
321 # http://www.pythonware.com/products/xml/sgmlop.htm
324 try:
325 import sgmlop
326 if not hasattr(sgmlop, "XMLParser"):
327 raise ImportError
328 except ImportError:
329 SgmlopParser = None # sgmlop accelerator not available
330 else:
331 class SgmlopParser:
332 def __init__(self, target):
334 # setup callbacks
335 self.finish_starttag = target.start
336 self.finish_endtag = target.end
337 self.handle_data = target.data
338 self.handle_xml = target.xml
340 # activate parser
341 self.parser = sgmlop.XMLParser()
342 self.parser.register(self)
343 self.feed = self.parser.feed
344 self.entity = {
345 "amp": "&", "gt": ">", "lt": "<",
346 "apos": "'", "quot": '"'
349 def close(self):
350 try:
351 self.parser.close()
352 finally:
353 self.parser = self.feed = None # nuke circular reference
355 def handle_proc(self, tag, attr):
356 import re
357 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
358 if m:
359 self.handle_xml(m.group(1), 1)
361 def handle_entityref(self, entity):
362 # <string> entity
363 try:
364 self.handle_data(self.entity[entity])
365 except KeyError:
366 self.handle_data("&%s;" % entity)
368 try:
369 from xml.parsers import expat
370 if not hasattr(expat, "ParserCreate"):
371 raise ImportError, "ParserCreate"
372 except ImportError:
373 ExpatParser = None
374 else:
375 class ExpatParser:
376 # fast expat parser for Python 2.0. this is about 50%
377 # slower than sgmlop, on roundtrip testing
378 def __init__(self, target):
379 self._parser = parser = expat.ParserCreate(None, None)
380 self._target = target
381 parser.StartElementHandler = target.start
382 parser.EndElementHandler = target.end
383 parser.CharacterDataHandler = target.data
384 encoding = None
385 if not parser.returns_unicode:
386 encoding = "utf-8"
387 target.xml(encoding, None)
389 def feed(self, data):
390 self._parser.Parse(data, 0)
392 def close(self):
393 self._parser.Parse("", 1) # end of data
394 del self._target, self._parser # get rid of circular references
396 class SlowParser:
397 """Default XML parser (based on xmllib.XMLParser)."""
398 # this is about 10 times slower than sgmlop, on roundtrip
399 # testing.
400 def __init__(self, target):
401 import xmllib # lazy subclassing (!)
402 if xmllib.XMLParser not in SlowParser.__bases__:
403 SlowParser.__bases__ = (xmllib.XMLParser,)
404 self.handle_xml = target.xml
405 self.unknown_starttag = target.start
406 self.handle_data = target.data
407 self.unknown_endtag = target.end
408 try:
409 xmllib.XMLParser.__init__(self, accept_utf8=1)
410 except TypeError:
411 xmllib.XMLParser.__init__(self) # pre-2.0
413 # --------------------------------------------------------------------
414 # XML-RPC marshalling and unmarshalling code
416 class Marshaller:
417 """Generate an XML-RPC params chunk from a Python data structure.
419 Create a Marshaller instance for each set of parameters, and use
420 the "dumps" method to convert your data (represented as a tuple)
421 to an XML-RPC params chunk. To write a fault response, pass a
422 Fault instance instead. You may prefer to use the "dumps" module
423 function for this purpose.
426 # by the way, if you don't understand what's going on in here,
427 # that's perfectly ok.
429 def __init__(self, encoding=None):
430 self.memo = {}
431 self.data = None
432 self.encoding = encoding
434 dispatch = {}
436 def dumps(self, values):
437 self.__out = []
438 self.write = write = self.__out.append
439 if isinstance(values, Fault):
440 # fault instance
441 write("<fault>\n")
442 self.__dump(vars(values))
443 write("</fault>\n")
444 else:
445 # parameter block
446 # FIXME: the xml-rpc specification allows us to leave out
447 # the entire <params> block if there are no parameters.
448 # however, changing this may break older code (including
449 # old versions of xmlrpclib.py), so this is better left as
450 # is for now. See @XMLRPC3 for more information. /F
451 write("<params>\n")
452 for v in values:
453 write("<param>\n")
454 self.__dump(v)
455 write("</param>\n")
456 write("</params>\n")
457 result = string.join(self.__out, "")
458 del self.__out, self.write # don't need this any more
459 return result
461 def __dump(self, value):
462 try:
463 f = self.dispatch[type(value)]
464 except KeyError:
465 raise TypeError, "cannot marshal %s objects" % type(value)
466 else:
467 f(self, value)
469 def dump_int(self, value):
470 # in case ints are > 32 bits
471 if value > MAXINT or value < MININT:
472 raise OverflowError, "int exceeds XML-RPC limits"
473 self.write("<value><int>%s</int></value>\n" % value)
474 dispatch[IntType] = dump_int
476 def dump_long(self, value):
477 # in case ints are > 32 bits
478 if value > MAXINT or value < MININT:
479 raise OverflowError, "long int exceeds XML-RPC limits"
480 self.write("<value><int>%s</int></value>\n" % int(value))
481 dispatch[LongType] = dump_long
483 def dump_double(self, value):
484 self.write("<value><double>%s</double></value>\n" % repr(value))
485 dispatch[FloatType] = dump_double
487 def dump_string(self, value, escape=escape):
488 self.write("<value><string>%s</string></value>\n" % escape(value))
489 dispatch[StringType] = dump_string
491 if unicode:
492 def dump_unicode(self, value, escape=escape):
493 value = value.encode(self.encoding)
494 self.write("<value><string>%s</string></value>\n" % escape(value))
495 dispatch[UnicodeType] = dump_unicode
497 def opencontainer(self, value):
498 if value:
499 i = id(value)
500 if self.memo.has_key(i):
501 raise TypeError, "cannot marshal recursive data structures"
502 self.memo[i] = None
504 def closecontainer(self, value):
505 if value:
506 del self.memo[id(value)]
508 def dump_array(self, value):
509 self.opencontainer(value)
510 write = self.write
511 dump = self.__dump
512 write("<value><array><data>\n")
513 for v in value:
514 dump(v)
515 write("</data></array></value>\n")
516 self.closecontainer(value)
517 dispatch[TupleType] = dump_array
518 dispatch[ListType] = dump_array
520 def dump_struct(self, value, escape=escape):
521 self.opencontainer(value)
522 write = self.write
523 dump = self.__dump
524 write("<value><struct>\n")
525 for k, v in value.items():
526 write("<member>\n")
527 if type(k) is not StringType:
528 raise TypeError, "dictionary key must be string"
529 write("<name>%s</name>\n" % escape(k))
530 dump(v)
531 write("</member>\n")
532 write("</struct></value>\n")
533 self.closecontainer(value)
534 dispatch[DictType] = dump_struct
536 def dump_instance(self, value):
537 # check for special wrappers
538 if value.__class__ in WRAPPERS:
539 value.encode(self)
540 else:
541 # store instance attributes as a struct (really?)
542 self.dump_struct(value.__dict__)
543 dispatch[InstanceType] = dump_instance
545 class Unmarshaller:
546 """Unmarshal an XML-RPC response, based on incoming XML event
547 messages (start, data, end). Call close() to get the resulting
548 data structure.
550 Note that this reader is fairly tolerant, and gladly accepts bogus
551 XML-RPC data without complaining (but not bogus XML).
554 # and again, if you don't understand what's going on in here,
555 # that's perfectly ok.
557 def __init__(self):
558 self._type = None
559 self._stack = []
560 self._marks = []
561 self._data = []
562 self._methodname = None
563 self._encoding = "utf-8"
564 self.append = self._stack.append
566 def close(self):
567 # return response tuple and target method
568 if self._type is None or self._marks:
569 raise ResponseError()
570 if self._type == "fault":
571 raise apply(Fault, (), self._stack[0])
572 return tuple(self._stack)
574 def getmethodname(self):
575 return self._methodname
578 # event handlers
580 def xml(self, encoding, standalone):
581 self._encoding = encoding
582 # FIXME: assert standalone == 1 ???
584 def start(self, tag, attrs):
585 # prepare to handle this element
586 if tag == "array" or tag == "struct":
587 self._marks.append(len(self._stack))
588 self._data = []
589 self._value = (tag == "value")
591 def data(self, text):
592 self._data.append(text)
594 def end(self, tag, join=string.join):
595 # call the appropriate end tag handler
596 try:
597 f = self.dispatch[tag]
598 except KeyError:
599 pass # unknown tag ?
600 else:
601 return f(self, join(self._data, ""))
604 # accelerator support
606 def end_dispatch(self, tag, data):
607 # dispatch data
608 try:
609 f = self.dispatch[tag]
610 except KeyError:
611 pass # unknown tag ?
612 else:
613 return f(self, data)
616 # element decoders
618 dispatch = {}
620 def end_boolean(self, data):
621 if data == "0":
622 self.append(False)
623 elif data == "1":
624 self.append(True)
625 else:
626 raise TypeError, "bad boolean value"
627 self._value = 0
628 dispatch["boolean"] = end_boolean
630 def end_int(self, data):
631 self.append(int(data))
632 self._value = 0
633 dispatch["i4"] = end_int
634 dispatch["int"] = end_int
636 def end_double(self, data):
637 self.append(float(data))
638 self._value = 0
639 dispatch["double"] = end_double
641 def end_string(self, data):
642 if self._encoding:
643 data = _decode(data, self._encoding)
644 self.append(_stringify(data))
645 self._value = 0
646 dispatch["string"] = end_string
647 dispatch["name"] = end_string # struct keys are always strings
649 def end_array(self, data):
650 mark = self._marks[-1]
651 del self._marks[-1]
652 # map arrays to Python lists
653 self._stack[mark:] = [self._stack[mark:]]
654 self._value = 0
655 dispatch["array"] = end_array
657 def end_struct(self, data):
658 mark = self._marks[-1]
659 del self._marks[-1]
660 # map structs to Python dictionaries
661 dict = {}
662 items = self._stack[mark:]
663 for i in range(0, len(items), 2):
664 dict[_stringify(items[i])] = items[i+1]
665 self._stack[mark:] = [dict]
666 self._value = 0
667 dispatch["struct"] = end_struct
669 def end_base64(self, data):
670 value = Binary()
671 value.decode(data)
672 self.append(value)
673 self._value = 0
674 dispatch["base64"] = end_base64
676 def end_dateTime(self, data):
677 value = DateTime()
678 value.decode(data)
679 self.append(value)
680 dispatch["dateTime.iso8601"] = end_dateTime
682 def end_value(self, data):
683 # if we stumble upon an value element with no internal
684 # elements, treat it as a string element
685 if self._value:
686 self.end_string(data)
687 dispatch["value"] = end_value
689 def end_params(self, data):
690 self._type = "params"
691 dispatch["params"] = end_params
693 def end_fault(self, data):
694 self._type = "fault"
695 dispatch["fault"] = end_fault
697 def end_methodName(self, data):
698 if self._encoding:
699 data = _decode(data, self._encoding)
700 self._methodname = data
701 self._type = "methodName" # no params
702 dispatch["methodName"] = end_methodName
705 # --------------------------------------------------------------------
706 # convenience functions
708 def getparser():
709 """getparser() -> parser, unmarshaller
711 Create an instance of the fastest available parser, and attach it
712 to an unmarshalling object. Return both objects.
714 if FastParser and FastUnmarshaller:
715 target = FastUnmarshaller(True, False, binary, datetime)
716 parser = FastParser(target)
717 else:
718 target = Unmarshaller()
719 if FastParser:
720 parser = FastParser(target)
721 elif SgmlopParser:
722 parser = SgmlopParser(target)
723 elif ExpatParser:
724 parser = ExpatParser(target)
725 else:
726 parser = SlowParser(target)
727 return parser, target
729 def dumps(params, methodname=None, methodresponse=None, encoding=None):
730 """data [,options] -> marshalled data
732 Convert an argument tuple or a Fault instance to an XML-RPC
733 request (or response, if the methodresponse option is used).
735 In addition to the data object, the following options can be given
736 as keyword arguments:
738 methodname: the method name for a methodCall packet
740 methodresponse: true to create a methodResponse packet.
741 If this option is used with a tuple, the tuple must be
742 a singleton (i.e. it can contain only one element).
744 encoding: the packet encoding (default is UTF-8)
746 All 8-bit strings in the data structure are assumed to use the
747 packet encoding. Unicode strings are automatically converted,
748 where necessary.
751 assert isinstance(params, TupleType) or isinstance(params, Fault),\
752 "argument must be tuple or Fault instance"
754 if isinstance(params, Fault):
755 methodresponse = 1
756 elif methodresponse and isinstance(params, TupleType):
757 assert len(params) == 1, "response tuple must be a singleton"
759 if not encoding:
760 encoding = "utf-8"
762 m = Marshaller(encoding)
763 data = m.dumps(params)
765 if encoding != "utf-8":
766 xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
767 else:
768 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
770 # standard XML-RPC wrappings
771 if methodname:
772 # a method call
773 if not isinstance(methodname, StringType):
774 methodname = methodname.encode(encoding)
775 data = (
776 xmlheader,
777 "<methodCall>\n"
778 "<methodName>", methodname, "</methodName>\n",
779 data,
780 "</methodCall>\n"
782 elif methodresponse:
783 # a method response, or a fault structure
784 data = (
785 xmlheader,
786 "<methodResponse>\n",
787 data,
788 "</methodResponse>\n"
790 else:
791 return data # return as is
792 return string.join(data, "")
794 def loads(data):
795 """data -> unmarshalled data, method name
797 Convert an XML-RPC packet to unmarshalled data plus a method
798 name (None if not present).
800 If the XML-RPC packet represents a fault condition, this function
801 raises a Fault exception.
803 p, u = getparser()
804 p.feed(data)
805 p.close()
806 return u.close(), u.getmethodname()
809 # --------------------------------------------------------------------
810 # request dispatcher
812 class _Method:
813 # some magic to bind an XML-RPC method to an RPC server.
814 # supports "nested" methods (e.g. examples.getStateName)
815 def __init__(self, send, name):
816 self.__send = send
817 self.__name = name
818 def __getattr__(self, name):
819 return _Method(self.__send, "%s.%s" % (self.__name, name))
820 def __call__(self, *args):
821 return self.__send(self.__name, args)
824 class Transport:
825 """Handles an HTTP transaction to an XML-RPC server."""
827 # client identifier (may be overridden)
828 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
830 def request(self, host, handler, request_body, verbose=0):
831 # issue XML-RPC request
833 h = self.make_connection(host)
834 if verbose:
835 h.set_debuglevel(1)
837 self.send_request(h, handler, request_body)
838 self.send_host(h, host)
839 self.send_user_agent(h)
840 self.send_content(h, request_body)
842 errcode, errmsg, headers = h.getreply()
844 if errcode != 200:
845 raise ProtocolError(
846 host + handler,
847 errcode, errmsg,
848 headers
851 self.verbose = verbose
853 return self.parse_response(h.getfile())
855 def getparser(self):
856 # get parser and unmarshaller
857 return getparser()
859 def make_connection(self, host):
860 # create a HTTP connection object from a host descriptor
861 import httplib
862 return httplib.HTTP(host)
864 def send_request(self, connection, handler, request_body):
865 connection.putrequest("POST", handler)
867 def send_host(self, connection, host):
868 connection.putheader("Host", host)
870 def send_user_agent(self, connection):
871 connection.putheader("User-Agent", self.user_agent)
873 def send_content(self, connection, request_body):
874 connection.putheader("Content-Type", "text/xml")
875 connection.putheader("Content-Length", str(len(request_body)))
876 connection.endheaders()
877 if request_body:
878 connection.send(request_body)
880 def parse_response(self, f):
881 # read response from input file, and parse it
883 p, u = self.getparser()
885 while 1:
886 response = f.read(1024)
887 if not response:
888 break
889 if self.verbose:
890 print "body:", repr(response)
891 p.feed(response)
893 f.close()
894 p.close()
896 return u.close()
898 class SafeTransport(Transport):
899 """Handles an HTTPS transaction to an XML-RPC server."""
901 def make_connection(self, host):
902 # create a HTTPS connection object from a host descriptor
903 # host may be a string, or a (host, x509-dict) tuple
904 import httplib
905 if isinstance(host, TupleType):
906 host, x509 = host
907 else:
908 x509 = {}
909 try:
910 HTTPS = httplib.HTTPS
911 except AttributeError:
912 raise NotImplementedError,\
913 "your version of httplib doesn't support HTTPS"
914 else:
915 return apply(HTTPS, (host, None), x509)
917 def send_host(self, connection, host):
918 if isinstance(host, TupleType):
919 host, x509 = host
920 connection.putheader("Host", host)
922 class ServerProxy:
923 """uri [,options] -> a logical connection to an XML-RPC server
925 uri is the connection point on the server, given as
926 scheme://host/target.
928 The standard implementation always supports the "http" scheme. If
929 SSL socket support is available (Python 2.0), it also supports
930 "https".
932 If the target part and the slash preceding it are both omitted,
933 "/RPC2" is assumed.
935 The following options can be given as keyword arguments:
937 transport: a transport factory
938 encoding: the request encoding (default is UTF-8)
940 All 8-bit strings passed to the server proxy are assumed to use
941 the given encoding.
944 def __init__(self, uri, transport=None, encoding=None, verbose=0):
945 # establish a "logical" server connection
947 # get the url
948 import urllib
949 type, uri = urllib.splittype(uri)
950 if type not in ("http", "https"):
951 raise IOError, "unsupported XML-RPC protocol"
952 self.__host, self.__handler = urllib.splithost(uri)
953 if not self.__handler:
954 self.__handler = "/RPC2"
956 if transport is None:
957 if type == "https":
958 transport = SafeTransport()
959 else:
960 transport = Transport()
961 self.__transport = transport
963 self.__encoding = encoding
964 self.__verbose = verbose
966 def __request(self, methodname, params):
967 # call a method on the remote server
969 request = dumps(params, methodname, encoding=self.__encoding)
971 response = self.__transport.request(
972 self.__host,
973 self.__handler,
974 request,
975 verbose=self.__verbose
978 if len(response) == 1:
979 response = response[0]
981 return response
983 def __repr__(self):
984 return (
985 "<ServerProxy for %s%s>" %
986 (self.__host, self.__handler)
989 __str__ = __repr__
991 def __getattr__(self, name):
992 # magic method dispatcher
993 return _Method(self.__request, name)
995 # note: to call a remote object with an non-standard name, use
996 # result getattr(server, "strange-python-name")(args)
998 # compatibility
999 Server = ServerProxy
1001 # --------------------------------------------------------------------
1002 # test code
1004 if __name__ == "__main__":
1006 # simple test program (from the XML-RPC specification)
1008 # server = ServerProxy("http://localhost:8000") # local server
1009 server = ServerProxy("http://betty.userland.com")
1011 print server
1013 try:
1014 print server.examples.getStateName(41)
1015 except Error, v:
1016 print "ERROR", v