2 # XML-RPC CLIENT LIBRARY
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.
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.
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.
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
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
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
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.
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
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"
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
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
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
)
144 def escape(s
, replace
=string
.replace
):
145 s
= replace(s
, "&", "&")
146 s
= replace(s
, "<", "<")
147 return replace(s
, ">", ">",)
153 def _stringify(string
):
154 # convert to 7-bit ascii if possible
160 def _stringify(string
):
163 __version__
= "1.0.0"
165 # --------------------------------------------------------------------
168 class Error(Exception):
169 """Base class for client errors."""
173 class ProtocolError(Error
):
174 """Indicates an HTTP protocol error."""
175 def __init__(self
, url
, errcode
, errmsg
, headers
):
178 self
.errcode
= errcode
180 self
.headers
= headers
183 "<ProtocolError for %s: %s %s>" %
184 (self
.url
, self
.errcode
, self
.errmsg
)
187 class ResponseError(Error
):
188 """Indicates a broken response package."""
192 """Indicates an XML-RPC fault package."""
193 def __init__(self
, faultCode
, faultString
, **extra
):
195 self
.faultCode
= faultCode
196 self
.faultString
= faultString
200 (self
.faultCode
, repr(self
.faultString
))
203 # --------------------------------------------------------------------
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
):
221 return cmp(self
.value
, other
)
225 return "<Boolean True at %x>" % id(self
)
227 return "<Boolean False at %x>" % id(self
)
232 def __nonzero__(self
):
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
)]
242 """DateTime wrapper for an ISO 8601 string or time tuple or
243 localtime integer value to generate 'dateTime.iso8601' XML-RPC
247 def __init__(self
, value
=0):
248 if not isinstance(value
, StringType
):
249 if not isinstance(value
, TupleType
):
252 value
= time
.localtime(value
)
253 value
= time
.strftime("%Y%m%dT%H:%M:%S", value
)
256 def __cmp__(self
, other
):
257 if isinstance(other
, DateTime
):
259 return cmp(self
.value
, other
)
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")
278 """Wrapper for binary data."""
280 def __init__(self
, data
=None):
283 def __cmp__(self
, other
):
284 if isinstance(other
, Binary
):
286 return cmp(self
.data
, other
)
288 def decode(self
, data
):
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")
303 WRAPPERS
= DateTime
, Binary
, Boolean
305 # --------------------------------------------------------------------
309 # optional xmlrpclib accelerator. for more information on this
310 # component, contact info@pythonware.com
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
326 if not hasattr(sgmlop
, "XMLParser"):
329 SgmlopParser
= None # sgmlop accelerator not available
332 def __init__(self
, target
):
335 self
.finish_starttag
= target
.start
336 self
.finish_endtag
= target
.end
337 self
.handle_data
= target
.data
338 self
.handle_xml
= target
.xml
341 self
.parser
= sgmlop
.XMLParser()
342 self
.parser
.register(self
)
343 self
.feed
= self
.parser
.feed
345 "amp": "&", "gt": ">", "lt": "<",
346 "apos": "'", "quot": '"'
353 self
.parser
= self
.feed
= None # nuke circular reference
355 def handle_proc(self
, tag
, attr
):
357 m
= re
.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr
)
359 self
.handle_xml(m
.group(1), 1)
361 def handle_entityref(self
, entity
):
364 self
.handle_data(self
.entity
[entity
])
366 self
.handle_data("&%s;" % entity
)
369 from xml
.parsers
import expat
370 if not hasattr(expat
, "ParserCreate"):
371 raise ImportError, "ParserCreate"
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
385 if not parser
.returns_unicode
:
387 target
.xml(encoding
, None)
389 def feed(self
, data
):
390 self
._parser
.Parse(data
, 0)
393 self
._parser
.Parse("", 1) # end of data
394 del self
._target
, self
._parser
# get rid of circular references
397 """Default XML parser (based on xmllib.XMLParser)."""
398 # this is about 10 times slower than sgmlop, on roundtrip
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
409 xmllib
.XMLParser
.__init
__(self
, accept_utf8
=1)
411 xmllib
.XMLParser
.__init
__(self
) # pre-2.0
413 # --------------------------------------------------------------------
414 # XML-RPC marshalling and unmarshalling code
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):
432 self
.encoding
= encoding
436 def dumps(self
, values
):
438 self
.write
= write
= self
.__out
.append
439 if isinstance(values
, Fault
):
442 self
.__dump
(vars(values
))
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
457 result
= string
.join(self
.__out
, "")
458 del self
.__out
, self
.write
# don't need this any more
461 def __dump(self
, value
):
463 f
= self
.dispatch
[type(value
)]
465 raise TypeError, "cannot marshal %s objects" % type(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
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
):
500 if self
.memo
.has_key(i
):
501 raise TypeError, "cannot marshal recursive data structures"
504 def closecontainer(self
, value
):
506 del self
.memo
[id(value
)]
508 def dump_array(self
, value
):
509 self
.opencontainer(value
)
512 write("<value><array><data>\n")
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
)
524 write("<value><struct>\n")
525 for k
, v
in value
.items():
527 if type(k
) is not StringType
:
528 raise TypeError, "dictionary key must be string"
529 write("<name>%s</name>\n" % escape(k
))
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
:
541 # store instance attributes as a struct (really?)
542 self
.dump_struct(value
.__dict
__)
543 dispatch
[InstanceType
] = dump_instance
546 """Unmarshal an XML-RPC response, based on incoming XML event
547 messages (start, data, end). Call close() to get the resulting
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.
562 self
._methodname
= None
563 self
._encoding
= "utf-8"
564 self
.append
= self
._stack
.append
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
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
))
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
597 f
= self
.dispatch
[tag
]
601 return f(self
, join(self
._data
, ""))
604 # accelerator support
606 def end_dispatch(self
, tag
, data
):
609 f
= self
.dispatch
[tag
]
620 def end_boolean(self
, data
):
626 raise TypeError, "bad boolean value"
628 dispatch
["boolean"] = end_boolean
630 def end_int(self
, data
):
631 self
.append(int(data
))
633 dispatch
["i4"] = end_int
634 dispatch
["int"] = end_int
636 def end_double(self
, data
):
637 self
.append(float(data
))
639 dispatch
["double"] = end_double
641 def end_string(self
, data
):
643 data
= _decode(data
, self
._encoding
)
644 self
.append(_stringify(data
))
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]
652 # map arrays to Python lists
653 self
._stack
[mark
:] = [self
._stack
[mark
:]]
655 dispatch
["array"] = end_array
657 def end_struct(self
, data
):
658 mark
= self
._marks
[-1]
660 # map structs to Python dictionaries
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]
667 dispatch
["struct"] = end_struct
669 def end_base64(self
, data
):
674 dispatch
["base64"] = end_base64
676 def end_dateTime(self
, data
):
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
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
):
695 dispatch
["fault"] = end_fault
697 def end_methodName(self
, data
):
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
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
)
718 target
= Unmarshaller()
720 parser
= FastParser(target
)
722 parser
= SgmlopParser(target
)
724 parser
= ExpatParser(target
)
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,
751 assert isinstance(params
, TupleType
) or isinstance(params
, Fault
),\
752 "argument must be tuple or Fault instance"
754 if isinstance(params
, Fault
):
756 elif methodresponse
and isinstance(params
, TupleType
):
757 assert len(params
) == 1, "response tuple must be a singleton"
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
)
768 xmlheader
= "<?xml version='1.0'?>\n" # utf-8 is default
770 # standard XML-RPC wrappings
773 if not isinstance(methodname
, StringType
):
774 methodname
= methodname
.encode(encoding
)
778 "<methodName>", methodname
, "</methodName>\n",
783 # a method response, or a fault structure
786 "<methodResponse>\n",
788 "</methodResponse>\n"
791 return data
# return as is
792 return string
.join(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.
806 return u
.close(), u
.getmethodname()
809 # --------------------------------------------------------------------
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
):
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
)
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
)
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()
851 self
.verbose
= verbose
853 return self
.parse_response(h
.getfile())
856 # get parser and unmarshaller
859 def make_connection(self
, host
):
860 # create a HTTP connection object from a host descriptor
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()
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()
886 response
= f
.read(1024)
890 print "body:", repr(response
)
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
905 if isinstance(host
, TupleType
):
910 HTTPS
= httplib
.HTTPS
911 except AttributeError:
912 raise NotImplementedError,\
913 "your version of httplib doesn't support HTTPS"
915 return apply(HTTPS
, (host
, None), x509
)
917 def send_host(self
, connection
, host
):
918 if isinstance(host
, TupleType
):
920 connection
.putheader("Host", host
)
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
932 If the target part and the slash preceding it are both omitted,
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
944 def __init__(self
, uri
, transport
=None, encoding
=None, verbose
=0):
945 # establish a "logical" server connection
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:
958 transport
= SafeTransport()
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(
975 verbose
=self
.__verbose
978 if len(response
) == 1:
979 response
= response
[0]
985 "<ServerProxy for %s%s>" %
986 (self
.__host
, self
.__handler
)
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)
1001 # --------------------------------------------------------------------
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")
1014 print server
.examples
.getStateName(41)