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)
35 # Copyright (c) 1999-2001 by Secret Labs AB.
36 # Copyright (c) 1999-2001 by Fredrik Lundh.
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
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
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)
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.
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
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"
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
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
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
)
152 def _stringify(string
):
153 # convert to 7-bit ascii if possible
159 def _stringify(string
):
162 __version__
= "1.0b3"
164 # --------------------------------------------------------------------
167 class Error(Exception):
168 """Base class for client errors."""
172 class ProtocolError(Error
):
173 """Indicates an HTTP protocol error."""
174 def __init__(self
, url
, errcode
, errmsg
, headers
):
177 self
.errcode
= errcode
179 self
.headers
= headers
182 "<ProtocolError for %s: %s %s>" %
183 (self
.url
, self
.errcode
, self
.errmsg
)
186 class ResponseError(Error
):
187 """Indicates a broken response package."""
191 """Indicates an XML-RPC fault package."""
192 def __init__(self
, faultCode
, faultString
, **extra
):
194 self
.faultCode
= faultCode
195 self
.faultString
= faultString
199 (self
.faultCode
, repr(self
.faultString
))
202 # --------------------------------------------------------------------
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
):
220 return cmp(self
.value
, other
)
224 return "<Boolean True at %x>" % id(self
)
226 return "<Boolean False at %x>" % id(self
)
231 def __nonzero__(self
):
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
)]
241 """DateTime wrapper for an ISO 8601 string or time tuple or
242 localtime integer value to generate 'dateTime.iso8601' XML-RPC
246 def __init__(self
, value
=0):
247 if not isinstance(value
, StringType
):
248 if not isinstance(value
, TupleType
):
251 value
= time
.localtime(value
)
252 value
= time
.strftime("%Y%m%dT%H:%M:%S", value
)
255 def __cmp__(self
, other
):
256 if isinstance(other
, DateTime
):
258 return cmp(self
.value
, other
)
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")
277 """Wrapper for binary data."""
279 def __init__(self
, data
=None):
282 def __cmp__(self
, other
):
283 if isinstance(other
, Binary
):
285 return cmp(self
.data
, other
)
287 def decode(self
, data
):
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")
302 WRAPPERS
= DateTime
, Binary
, Boolean
304 # --------------------------------------------------------------------
308 # optional xmlrpclib accelerator. for more information on this
309 # component, contact info@pythonware.com
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
325 if not hasattr(sgmlop
, "XMLParser"):
328 SgmlopParser
= None # sgmlop accelerator not available
331 def __init__(self
, target
):
334 self
.finish_starttag
= target
.start
335 self
.finish_endtag
= target
.end
336 self
.handle_data
= target
.data
337 self
.handle_xml
= target
.xml
340 self
.parser
= sgmlop
.XMLParser()
341 self
.parser
.register(self
)
342 self
.feed
= self
.parser
.feed
344 "amp": "&", "gt": ">", "lt": "<",
345 "apos": "'", "quot": '"'
352 self
.parser
= self
.feed
= None # nuke circular reference
354 def handle_proc(self
, tag
, attr
):
356 m
= re
.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr
)
358 self
.handle_xml(m
.group(1), 1)
360 def handle_entityref(self
, entity
):
363 self
.handle_data(self
.entity
[entity
])
365 self
.handle_data("&%s;" % entity
)
368 from xml
.parsers
import expat
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
382 if not parser
.returns_unicode
:
384 target
.xml(encoding
, None)
386 def feed(self
, data
):
387 self
._parser
.Parse(data
, 0)
390 self
._parser
.Parse("", 1) # end of data
391 del self
._target
, self
._parser
# get rid of circular references
394 """Default XML parser (based on xmllib.XMLParser)."""
395 # this is about 10 times slower than sgmlop, on roundtrip
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
406 xmllib
.XMLParser
.__init
__(self
, accept_utf8
=1)
408 xmllib
.XMLParser
.__init
__(self
) # pre-2.0
410 # --------------------------------------------------------------------
411 # XML-RPC marshalling and unmarshalling code
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):
429 self
.encoding
= encoding
433 def dumps(self
, values
):
435 self
.write
= write
= self
.__out
.append
436 if isinstance(values
, Fault
):
439 self
.__dump
(vars(values
))
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
454 result
= string
.join(self
.__out
, "")
455 del self
.__out
, self
.write
# don't need this any more
458 def __dump(self
, value
):
460 f
= self
.dispatch
[type(value
)]
462 raise TypeError, "cannot marshal %s objects" % type(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
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
):
489 if self
.memo
.has_key(i
):
490 raise TypeError, "cannot marshal recursive data structures"
493 def dump_array(self
, value
):
494 self
.container(value
)
496 write("<value><array><data>\n")
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
)
506 write("<value><struct>\n")
507 for k
, v
in value
.items():
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
))
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
:
523 # store instance attributes as a struct (really?)
524 self
.dump_struct(value
.__dict
__)
525 dispatch
[InstanceType
] = dump_instance
528 """Unmarshal an XML-RPC response, based on incoming XML event
529 messages (start, data, end). Call close() to get the resulting
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.
544 self
._methodname
= None
545 self
._encoding
= "utf-8"
546 self
.append
= self
._stack
.append
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
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
))
571 self
._value
= (tag
== "value")
573 def data(self
, text
):
574 self
._data
.append(text
)
577 # call the appropriate end tag handler
579 f
= self
.dispatch
[tag
]
583 return f(self
, self
._data
)
586 # accelerator support
588 def end_dispatch(self
, tag
, data
):
591 f
= self
.dispatch
[tag
]
602 def end_boolean(self
, data
, join
=string
.join
):
603 data
= join(data
, "")
609 raise TypeError, "bad boolean value"
611 dispatch
["boolean"] = end_boolean
613 def end_int(self
, data
, join
=string
.join
):
614 self
.append(int(join(data
, "")))
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
, "")))
622 dispatch
["double"] = end_double
624 def end_string(self
, data
, join
=string
.join
):
625 data
= join(data
, "")
627 data
= _decode(data
, self
._encoding
)
628 self
.append(_stringify(data
))
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]
636 # map arrays to Python lists
637 self
._stack
[mark
:] = [self
._stack
[mark
:]]
639 dispatch
["array"] = end_array
641 def end_struct(self
, data
):
642 mark
= self
._marks
[-1]
644 # map structs to Python dictionaries
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]
651 dispatch
["struct"] = end_struct
653 def end_base64(self
, data
, join
=string
.join
):
655 value
.decode(join(data
, ""))
658 dispatch
["base64"] = end_base64
660 def end_dateTime(self
, data
, join
=string
.join
):
662 value
.decode(join(data
, ""))
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
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
):
679 dispatch
["fault"] = end_fault
681 def end_methodName(self
, data
, join
=string
.join
):
682 data
= join(data
, "")
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
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
)
703 target
= Unmarshaller()
705 parser
= FastParser(target
)
707 parser
= SgmlopParser(target
)
709 parser
= ExpatParser(target
)
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,
736 assert isinstance(params
, TupleType
) or isinstance(params
, Fault
),\
737 "argument must be tuple or Fault instance"
739 if isinstance(params
, Fault
):
741 elif methodresponse
and isinstance(params
, TupleType
):
742 assert len(params
) == 1, "response tuple must be a singleton"
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
)
753 xmlheader
= "<?xml version='1.0'?>\n" # utf-8 is default
755 # standard XML-RPC wrappings
758 if not isinstance(methodname
, StringType
):
759 methodname
= methodname
.encode(encoding
)
763 "<methodName>", methodname
, "</methodName>\n",
768 # a method response, or a fault structure
771 "<methodResponse>\n",
773 "</methodResponse>\n"
776 return data
# return as is
777 return string
.join(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.
791 return u
.close(), u
.getmethodname()
794 # --------------------------------------------------------------------
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
):
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
)
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
)
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()
836 self
.verbose
= verbose
838 return self
.parse_response(h
.getfile())
841 # get parser and unmarshaller
844 def make_connection(self
, host
):
845 # create a HTTP connection object from a host descriptor
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()
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()
871 response
= f
.read(1024)
875 print "body:", repr(response
)
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
890 if isinstance(host
, TupleType
):
895 HTTPS
= httplib
.HTTPS
896 except AttributeError:
897 raise NotImplementedError,\
898 "your version of httplib doesn't support HTTPS"
900 return apply(HTTPS
, (host
, None), x509
)
902 def send_host(self
, connection
, host
):
903 if isinstance(host
, TupleType
):
905 connection
.putheader("Host", host
)
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
917 If the target part and the slash preceding it are both omitted,
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
929 def __init__(self
, uri
, transport
=None, encoding
=None, verbose
=0):
930 # establish a "logical" server connection
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:
943 transport
= SafeTransport()
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(
960 verbose
=self
.__verbose
963 if len(response
) == 1:
964 response
= response
[0]
970 "<ServerProxy for %s%s>" %
971 (self
.__host
, self
.__handler
)
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)
986 # --------------------------------------------------------------------
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")
999 print server
.examples
.getStateName(41)