py-cvs-rel2_1 (Rev 1.2) merge
[python/dscho.git] / Lib / xmlrpclib.py
blob51a974ba4d1059787b88a8eab325126768e9e123
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
32 # Copyright (c) 1999-2001 by Secret Labs AB.
33 # Copyright (c) 1999-2001 by Fredrik Lundh.
35 # info@pythonware.com
36 # http://www.pythonware.com
38 # --------------------------------------------------------------------
39 # The XML-RPC client interface is
41 # Copyright (c) 1999-2001 by Secret Labs AB
42 # Copyright (c) 1999-2001 by Fredrik Lundh
44 # By obtaining, using, and/or copying this software and/or its
45 # associated documentation, you agree that you have read, understood,
46 # and will comply with the following terms and conditions:
48 # Permission to use, copy, modify, and distribute this software and
49 # its associated documentation for any purpose and without fee is
50 # hereby granted, provided that the above copyright notice appears in
51 # all copies, and that both that copyright notice and this permission
52 # notice appear in supporting documentation, and that the name of
53 # Secret Labs AB or the author not be used in advertising or publicity
54 # pertaining to distribution of the software without specific, written
55 # prior permission.
57 # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
58 # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
59 # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
60 # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
61 # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
62 # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
63 # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
64 # OF THIS SOFTWARE.
65 # --------------------------------------------------------------------
68 # things to look into before 1.0 final:
70 # TODO: unicode marshalling -DONE
71 # TODO: ascii-compatible encoding support -DONE
72 # TODO: safe transport -DONE (but mostly untested)
73 # TODO: sgmlop memory leak -DONE
74 # TODO: sgmlop xml parsing -DONE
75 # TODO: support unicode method names -DONE
76 # TODO: update selftest -DONE
77 # TODO: add docstrings -DONE
78 # TODO: clean up parser encoding (trust the parser) -DONE
79 # TODO: expat support -DONE
80 # TODO: _xmlrpclib accelerator support -DONE
81 # TODO: use smarter/faster escape from effdom
82 # TODO: support basic authentication (see robin's patch)
83 # TODO: fix host tuple handling in the server constructor
84 # TODO: let transport verify schemes
85 # TODO: update documentation
86 # TODO: authentication plugins
87 # TODO: memo problem (see HP's mail)
89 import re, string, time, operator
90 import urllib, xmllib
91 from types import *
92 from cgi import escape
94 try:
95 unicode
96 except NameError:
97 unicode = None # unicode support not available
99 def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
100 # decode non-ascii string (if possible)
101 if unicode and encoding and is8bit(data):
102 data = unicode(data, encoding)
103 return data
105 if unicode:
106 def _stringify(string):
107 # convert to 7-bit ascii if possible
108 try:
109 return str(string)
110 except UnicodeError:
111 return string
112 else:
113 def _stringify(string):
114 return string
116 __version__ = "1.0b2"
118 # --------------------------------------------------------------------
119 # Exceptions
121 class Error:
122 # base class for client errors
123 pass
125 class ProtocolError(Error):
126 # indicates an HTTP protocol error
127 def __init__(self, url, errcode, errmsg, headers):
128 self.url = url
129 self.errcode = errcode
130 self.errmsg = errmsg
131 self.headers = headers
132 def __repr__(self):
133 return (
134 "<ProtocolError for %s: %s %s>" %
135 (self.url, self.errcode, self.errmsg)
138 class ResponseError(Error):
139 # indicates a broken response package
140 pass
142 class Fault(Error):
143 # indicates a XML-RPC fault package
144 def __init__(self, faultCode, faultString, **extra):
145 self.faultCode = faultCode
146 self.faultString = faultString
147 def __repr__(self):
148 return (
149 "<Fault %s: %s>" %
150 (self.faultCode, repr(self.faultString))
153 # --------------------------------------------------------------------
154 # Special values
156 # boolean wrapper
157 # use True or False to generate a "boolean" XML-RPC value
159 class Boolean:
161 def __init__(self, value = 0):
162 self.value = operator.truth(value)
164 def encode(self, out):
165 out.write("<value><boolean>%d</boolean></value>\n" % self.value)
167 def __cmp__(self, other):
168 if isinstance(other, Boolean):
169 other = other.value
170 return cmp(self.value, other)
172 def __repr__(self):
173 if self.value:
174 return "<Boolean True at %x>" % id(self)
175 else:
176 return "<Boolean False at %x>" % id(self)
178 def __int__(self):
179 return self.value
181 def __nonzero__(self):
182 return self.value
184 True, False = Boolean(1), Boolean(0)
186 def boolean(value, truefalse=(False, True)):
187 # convert any Python value to XML-RPC boolean
188 return truefalse[operator.truth(value)]
191 # dateTime wrapper
192 # wrap your iso8601 string or time tuple or localtime integer value
193 # in this class to generate a "dateTime.iso8601" XML-RPC value
195 class DateTime:
197 def __init__(self, value=0):
198 t = type(value)
199 if not isinstance(t, StringType):
200 if not isinstance(t, TupleType):
201 if value == 0:
202 value = time.time()
203 value = time.localtime(value)
204 value = time.strftime("%Y%m%dT%H:%M:%S", value)
205 self.value = value
207 def __cmp__(self, other):
208 if isinstance(other, DateTime):
209 other = other.value
210 return cmp(self.value, other)
212 def __repr__(self):
213 return "<DateTime %s at %x>" % (self.value, id(self))
215 def decode(self, data):
216 self.value = string.strip(data)
218 def encode(self, out):
219 out.write("<value><dateTime.iso8601>")
220 out.write(self.value)
221 out.write("</dateTime.iso8601></value>\n")
223 def datetime(data):
224 value = DateTime()
225 value.decode(data)
226 return value
229 # binary data wrapper
231 class Binary:
233 def __init__(self, data=None):
234 self.data = data
236 def __cmp__(self, other):
237 if isinstance(other, Binary):
238 other = other.data
239 return cmp(self.data, other)
241 def decode(self, data):
242 import base64
243 self.data = base64.decodestring(data)
245 def encode(self, out):
246 import base64, StringIO
247 out.write("<value><base64>\n")
248 base64.encode(StringIO.StringIO(self.data), out)
249 out.write("</base64></value>\n")
251 def binary(data):
252 value = Binary()
253 value.decode(data)
254 return value
256 WRAPPERS = DateTime, Binary, Boolean
258 # --------------------------------------------------------------------
259 # XML parsers
261 try:
262 # optional xmlrpclib accelerator. for more information on this
263 # component, contact info@pythonware.com
264 import _xmlrpclib
265 FastParser = _xmlrpclib.Parser
266 FastUnmarshaller = _xmlrpclib.Unmarshaller
267 except (AttributeError, ImportError):
268 FastParser = FastUnmarshaller = None
271 # the SGMLOP parser is about 15x faster than Python's builtin
272 # XML parser. SGMLOP sources can be downloaded from:
274 # http://www.pythonware.com/products/xml/sgmlop.htm
277 try:
278 import sgmlop
279 if not hasattr(sgmlop, "XMLParser"):
280 raise ImportError
281 except ImportError:
282 SgmlopParser = None # sgmlop accelerator not available
283 else:
284 class SgmlopParser:
285 def __init__(self, target):
287 # setup callbacks
288 self.finish_starttag = target.start
289 self.finish_endtag = target.end
290 self.handle_data = target.data
291 self.handle_xml = target.xml
293 # activate parser
294 self.parser = sgmlop.XMLParser()
295 self.parser.register(self)
296 self.feed = self.parser.feed
297 self.entity = {
298 "amp": "&", "gt": ">", "lt": "<",
299 "apos": "'", "quot": '"'
302 def close(self):
303 try:
304 self.parser.close()
305 finally:
306 self.parser = self.feed = None # nuke circular reference
308 def handle_proc(self, tag, attr):
309 m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
310 if m:
311 self.handle_xml(m.group(1), 1)
313 def handle_entityref(self, entity):
314 # <string> entity
315 try:
316 self.handle_data(self.entity[entity])
317 except KeyError:
318 self.handle_data("&%s;" % entity)
320 try:
321 from xml.parsers import expat
322 except ImportError:
323 ExpatParser = None
324 else:
325 class ExpatParser:
326 # fast expat parser for Python 2.0. this is about 50%
327 # slower than sgmlop, on roundtrip testing
328 def __init__(self, target):
329 self._parser = parser = expat.ParserCreate(None, None)
330 self._target = target
331 parser.StartElementHandler = target.start
332 parser.EndElementHandler = target.end
333 parser.CharacterDataHandler = target.data
334 encoding = None
335 if not parser.returns_unicode:
336 encoding = "utf-8"
337 target.xml(encoding, None)
339 def feed(self, data):
340 self._parser.Parse(data, 0)
342 def close(self):
343 self._parser.Parse("", 1) # end of data
344 del self._target, self._parser # get rid of circular references
346 class SlowParser(xmllib.XMLParser):
347 # slow but safe standard parser, based on the XML parser in
348 # Python's standard library. this is about 10 times slower
349 # than sgmlop, on roundtrip testing.
350 def __init__(self, target):
351 self.handle_xml = target.xml
352 self.unknown_starttag = target.start
353 self.handle_data = target.data
354 self.unknown_endtag = target.end
355 xmllib.XMLParser.__init__(self)
358 # --------------------------------------------------------------------
359 # XML-RPC marshalling and unmarshalling code
361 class Marshaller:
362 """Generate an XML-RPC params chunk from a Python data structure"""
364 # USAGE: create a marshaller instance for each set of parameters,
365 # and use "dumps" to convert your data (represented as a tuple) to
366 # a XML-RPC params chunk. to write a fault response, pass a Fault
367 # instance instead. you may prefer to use the "dumps" convenience
368 # function for this purpose (see below).
370 # by the way, if you don't understand what's going on in here,
371 # that's perfectly ok.
373 def __init__(self, encoding=None):
374 self.memo = {}
375 self.data = None
376 self.encoding = encoding
378 dispatch = {}
380 def dumps(self, values):
381 self.__out = []
382 self.write = write = self.__out.append
383 if isinstance(values, Fault):
384 # fault instance
385 write("<fault>\n")
386 self.__dump(vars(values))
387 write("</fault>\n")
388 else:
389 # parameter block
390 write("<params>\n")
391 for v in values:
392 write("<param>\n")
393 self.__dump(v)
394 write("</param>\n")
395 write("</params>\n")
396 result = string.join(self.__out, "")
397 del self.__out, self.write # don't need this any more
398 return result
400 def __dump(self, value):
401 try:
402 f = self.dispatch[type(value)]
403 except KeyError:
404 raise TypeError, "cannot marshal %s objects" % type(value)
405 else:
406 f(self, value)
408 def dump_int(self, value):
409 self.write("<value><int>%s</int></value>\n" % value)
410 dispatch[IntType] = dump_int
412 def dump_double(self, value):
413 self.write("<value><double>%s</double></value>\n" % value)
414 dispatch[FloatType] = dump_double
416 def dump_string(self, value):
417 self.write("<value><string>%s</string></value>\n" % escape(value))
418 dispatch[StringType] = dump_string
420 if unicode:
421 def dump_unicode(self, value):
422 value = value.encode(self.encoding)
423 self.write("<value><string>%s</string></value>\n" % escape(value))
424 dispatch[UnicodeType] = dump_unicode
426 def container(self, value):
427 if value:
428 i = id(value)
429 if self.memo.has_key(i):
430 raise TypeError, "cannot marshal recursive data structures"
431 self.memo[i] = None
433 def dump_array(self, value):
434 self.container(value)
435 write = self.write
436 write("<value><array><data>\n")
437 for v in value:
438 self.__dump(v)
439 write("</data></array></value>\n")
440 dispatch[TupleType] = dump_array
441 dispatch[ListType] = dump_array
443 def dump_struct(self, value):
444 self.container(value)
445 write = self.write
446 write("<value><struct>\n")
447 for k, v in value.items():
448 write("<member>\n")
449 if type(k) is not StringType:
450 raise TypeError, "dictionary key must be string"
451 write("<name>%s</name>\n" % escape(k))
452 self.__dump(v)
453 write("</member>\n")
454 write("</struct></value>\n")
455 dispatch[DictType] = dump_struct
457 def dump_instance(self, value):
458 # check for special wrappers
459 if value.__class__ in WRAPPERS:
460 value.encode(self)
461 else:
462 # store instance attributes as a struct (really?)
463 self.dump_struct(value.__dict__)
464 dispatch[InstanceType] = dump_instance
466 class Unmarshaller:
468 # unmarshal an XML-RPC response, based on incoming XML event
469 # messages (start, data, end). call close to get the resulting
470 # data structure
472 # note that this reader is fairly tolerant, and gladly accepts
473 # bogus XML-RPC data without complaining (but not bogus XML).
475 # and again, if you don't understand what's going on in here,
476 # that's perfectly ok.
478 def __init__(self):
479 self._type = None
480 self._stack = []
481 self._marks = []
482 self._data = []
483 self._methodname = None
484 self._encoding = "utf-8"
485 self.append = self._stack.append
487 def close(self):
488 # return response tuple and target method
489 if self._type is None or self._marks:
490 raise ResponseError()
491 if self._type == "fault":
492 raise apply(Fault, (), self._stack[0])
493 return tuple(self._stack)
495 def getmethodname(self):
496 return self._methodname
499 # event handlers
501 def xml(self, encoding, standalone):
502 self._encoding = encoding
503 # FIXME: assert standalone == 1 ???
505 def start(self, tag, attrs):
506 # prepare to handle this element
507 if tag == "array" or tag == "struct":
508 self._marks.append(len(self._stack))
509 self._data = []
510 self._value = (tag == "value")
512 def data(self, text):
513 self._data.append(text)
515 def end(self, tag):
516 # call the appropriate end tag handler
517 try:
518 f = self.dispatch[tag]
519 except KeyError:
520 pass # unknown tag ?
521 else:
522 return f(self, self._data)
525 # accelerator support
527 def end_dispatch(self, tag, data):
528 # dispatch data
529 try:
530 f = self.dispatch[tag]
531 except KeyError:
532 pass # unknown tag ?
533 else:
534 return f(self, data)
537 # element decoders
539 dispatch = {}
541 def end_boolean(self, data, join=string.join):
542 data = join(data, "")
543 if data == "0":
544 self.append(False)
545 elif data == "1":
546 self.append(True)
547 else:
548 raise TypeError, "bad boolean value"
549 self._value = 0
550 dispatch["boolean"] = end_boolean
552 def end_int(self, data, join=string.join):
553 self.append(int(join(data, "")))
554 self._value = 0
555 dispatch["i4"] = end_int
556 dispatch["int"] = end_int
558 def end_double(self, data, join=string.join):
559 self.append(float(join(data, "")))
560 self._value = 0
561 dispatch["double"] = end_double
563 def end_string(self, data, join=string.join):
564 data = join(data, "")
565 if self._encoding:
566 data = _decode(data, self._encoding)
567 self.append(_stringify(data))
568 self._value = 0
569 dispatch["string"] = end_string
570 dispatch["name"] = end_string # struct keys are always strings
572 def end_array(self, data):
573 mark = self._marks[-1]
574 del self._marks[-1]
575 # map arrays to Python lists
576 self._stack[mark:] = [self._stack[mark:]]
577 self._value = 0
578 dispatch["array"] = end_array
580 def end_struct(self, data):
581 mark = self._marks[-1]
582 del self._marks[-1]
583 # map structs to Python dictionaries
584 dict = {}
585 items = self._stack[mark:]
586 for i in range(0, len(items), 2):
587 dict[_stringify(items[i])] = items[i+1]
588 self._stack[mark:] = [dict]
589 self._value = 0
590 dispatch["struct"] = end_struct
592 def end_base64(self, data, join=string.join):
593 value = Binary()
594 value.decode(join(data, ""))
595 self.append(value)
596 self._value = 0
597 dispatch["base64"] = end_base64
599 def end_dateTime(self, data, join=string.join):
600 value = DateTime()
601 value.decode(join(data, ""))
602 self.append(value)
603 dispatch["dateTime.iso8601"] = end_dateTime
605 def end_value(self, data):
606 # if we stumble upon an value element with no internal
607 # elements, treat it as a string element
608 if self._value:
609 self.end_string(data)
610 dispatch["value"] = end_value
612 def end_params(self, data):
613 self._type = "params"
614 dispatch["params"] = end_params
616 def end_fault(self, data):
617 self._type = "fault"
618 dispatch["fault"] = end_fault
620 def end_methodName(self, data, join=string.join):
621 data = join(data, "")
622 if self._encoding:
623 data = _decode(data, self._encoding)
624 self._methodname = data
625 self._type = "methodName" # no params
626 dispatch["methodName"] = end_methodName
629 # --------------------------------------------------------------------
630 # convenience functions
632 def getparser():
633 """getparser() -> parser, unmarshaller
635 Create an instance of the fastest available parser, and attach
636 it to an unmarshalling object. Return both objects.
638 if FastParser and FastUnmarshaller:
639 target = FastUnmarshaller(True, False, binary, datetime)
640 parser = FastParser(target)
641 else:
642 target = Unmarshaller()
643 if FastParser:
644 parser = FastParser(target)
645 elif SgmlopParser:
646 parser = SgmlopParser(target)
647 elif ExpatParser:
648 parser = ExpatParser(target)
649 else:
650 parser = SlowParser(target)
651 return parser, target
653 def dumps(params, methodname=None, methodresponse=None, encoding=None):
654 """data [,options] -> marshalled data
656 Convert an argument tuple or a Fault instance to an XML-RPC
657 request (or response, if the methodresponse option is used).
659 In addition to the data object, the following options can be
660 given as keyword arguments:
662 methodname: the method name for a methodCall packet
664 methodresponse: true to create a methodResponse packet.
665 If this option is used with a tuple, the tuple must be
666 a singleton (i.e. it can contain only one element).
668 encoding: the packet encoding (default is UTF-8)
670 All 8-bit strings in the data structure are assumed to use the
671 packet encoding. Unicode strings are automatically converted,
672 as necessary.
675 assert isinstance(params, TupleType) or isinstance(params, Fault),\
676 "argument must be tuple or Fault instance"
678 if isinstance(params, Fault):
679 methodresponse = 1
680 elif methodresponse and isinstance(params, TupleType):
681 assert len(params) == 1, "response tuple must be a singleton"
683 if not encoding:
684 encoding = "utf-8"
686 m = Marshaller(encoding)
687 data = m.dumps(params)
689 if encoding != "utf-8":
690 xmlheader = "<?xml version='1.0' encoding=%s?>\n" % repr(encoding)
691 else:
692 xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
694 # standard XML-RPC wrappings
695 if methodname:
696 # a method call
697 if not isinstance(methodname, StringType):
698 methodname = methodname.encode(encoding)
699 data = (
700 xmlheader,
701 "<methodCall>\n"
702 "<methodName>", methodname, "</methodName>\n",
703 data,
704 "</methodCall>\n"
706 elif methodresponse:
707 # a method response, or a fault structure
708 data = (
709 xmlheader,
710 "<methodResponse>\n",
711 data,
712 "</methodResponse>\n"
714 else:
715 return data # return as is
716 return string.join(data, "")
718 def loads(data):
719 """data -> unmarshalled data, method name
721 Convert an XML-RPC packet to unmarshalled data plus a method
722 name (None if not present).
724 If the XML-RPC packet represents a fault condition, this function
725 raises a Fault exception.
727 p, u = getparser()
728 p.feed(data)
729 p.close()
730 return u.close(), u.getmethodname()
733 # --------------------------------------------------------------------
734 # request dispatcher
736 class _Method:
737 # some magic to bind an XML-RPC method to an RPC server.
738 # supports "nested" methods (e.g. examples.getStateName)
739 def __init__(self, send, name):
740 self.__send = send
741 self.__name = name
742 def __getattr__(self, name):
743 return _Method(self.__send, "%s.%s" % (self.__name, name))
744 def __call__(self, *args):
745 return self.__send(self.__name, args)
748 class Transport:
749 """Handles an HTTP transaction to an XML-RPC server"""
751 # client identifier (may be overridden)
752 user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
754 def request(self, host, handler, request_body, verbose=0):
755 # issue XML-RPC request
757 h = self.make_connection(host)
758 if verbose:
759 h.set_debuglevel(1)
761 self.send_request(h, handler, request_body)
762 self.send_host(h, host)
763 self.send_user_agent(h)
764 self.send_content(h, request_body)
766 errcode, errmsg, headers = h.getreply()
768 if errcode != 200:
769 raise ProtocolError(
770 host + handler,
771 errcode, errmsg,
772 headers
775 self.verbose = verbose
777 return self.parse_response(h.getfile())
779 def make_connection(self, host):
780 # create a HTTP connection object from a host descriptor
781 import httplib
782 return httplib.HTTP(host)
784 def send_request(self, connection, handler, request_body):
785 connection.putrequest("POST", handler)
787 def send_host(self, connection, host):
788 connection.putheader("Host", host)
790 def send_user_agent(self, connection):
791 connection.putheader("User-Agent", self.user_agent)
793 def send_content(self, connection, request_body):
794 connection.putheader("Content-Type", "text/xml")
795 connection.putheader("Content-Length", str(len(request_body)))
796 connection.endheaders()
797 if request_body:
798 connection.send(request_body)
800 def parse_response(self, f):
801 # read response from input file, and parse it
803 p, u = getparser()
805 while 1:
806 response = f.read(1024)
807 if not response:
808 break
809 if self.verbose:
810 print "body:", repr(response)
811 p.feed(response)
813 f.close()
814 p.close()
816 return u.close()
818 class SafeTransport(Transport):
819 """Handles an HTTPS transaction to an XML-RPC server"""
821 def make_connection(self, host):
822 # create a HTTPS connection object from a host descriptor
823 # host may be a string, or a (host, x509-dict) tuple
824 import httplib
825 if isinstance(host, TupleType):
826 host, x509 = host
827 else:
828 x509 = {}
829 try:
830 HTTPS = httplib.HTTPS
831 except AttributeError:
832 raise NotImplementedError,\
833 "your version of httplib doesn't support HTTPS"
834 else:
835 return apply(HTTPS, (host, None), x509)
837 def send_host(self, connection, host):
838 if isinstance(host, TupleType):
839 host, x509 = host
840 connection.putheader("Host", host)
842 class ServerProxy:
843 """uri [,options] -> a logical connection to an XML-RPC server
845 uri is the connection point on the server, given as
846 scheme://host/target.
848 The standard implementation always supports the "http" scheme. If
849 SSL socket support is available (Python 2.0), it also supports
850 "https".
852 If the target part and the slash preceding it are both omitted,
853 "/RPC2" is assumed.
855 The following options can be given as keyword arguments:
857 transport: a transport factory
858 encoding: the request encoding (default is UTF-8)
860 All 8-bit strings passed to the server proxy are assumed to use
861 the given encoding.
864 def __init__(self, uri, transport=None, encoding=None, verbose=0):
865 # establish a "logical" server connection
867 # get the url
868 type, uri = urllib.splittype(uri)
869 if type not in ("http", "https"):
870 raise IOError, "unsupported XML-RPC protocol"
871 self.__host, self.__handler = urllib.splithost(uri)
872 if not self.__handler:
873 self.__handler = "/RPC2"
875 if transport is None:
876 if type == "https":
877 transport = SafeTransport()
878 else:
879 transport = Transport()
880 self.__transport = transport
882 self.__encoding = encoding
883 self.__verbose = verbose
885 def __request(self, methodname, params):
886 # call a method on the remote server
888 request = dumps(params, methodname, encoding=self.__encoding)
890 response = self.__transport.request(
891 self.__host,
892 self.__handler,
893 request,
894 verbose=self.__verbose
897 if len(response) == 1:
898 response = response[0]
900 return response
902 def __repr__(self):
903 return (
904 "<Server proxy for %s%s>" %
905 (self.__host, self.__handler)
908 __str__ = __repr__
910 def __getattr__(self, name):
911 # magic method dispatcher
912 return _Method(self.__request, name)
914 # note: to call a remote object with an non-standard name, use
915 # result getattr(server, "strange-python-name")(args)
917 Server = ServerProxy
919 # --------------------------------------------------------------------
920 # test code
922 if __name__ == "__main__":
924 # simple test program (from the XML-RPC specification)
926 # server = Server("http://localhost:8000") # local server
927 server = Server("http://betty.userland.com")
929 print server
931 try:
932 print server.examples.getStateName(41)
933 except Error, v:
934 print "ERROR", v