/[cvs]/nfo/projects/netfraggle/libs/xmlrpclib.py
ViewVC logotype

Annotation of /nfo/projects/netfraggle/libs/xmlrpclib.py

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (hide annotations)
Tue Aug 31 16:55:34 2004 UTC (20 years, 2 months ago) by joko
Branch: MAIN
CVS Tags: HEAD
Changes since 1.4: +2 -2 lines
File MIME type: text/x-python
+? minor fix with some FaultObject

1 joko 1.1 #
2     # XML-RPC CLIENT LIBRARY
3 joko 1.5 # $Id: xmlrpclib.py,v 1.4 2004/08/31 02:24:28 joko Exp $
4 joko 1.1 #
5     # an XML-RPC client interface for Python.
6     #
7     # the marshalling and response parser code can also be used to
8     # implement XML-RPC servers.
9     #
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.
15     #
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-02 fl More dumps microtuning
37     # 2001-10-04 fl Make sure import expat gets a parser (from Guido van Rossum)
38     # 2001-10-10 sm Allow long ints to be passed as ints if they don't overflow
39     # 2001-10-17 sm Test for int and long overflow (allows use on 64-bit systems)
40     # 2001-11-12 fl Use repr() to marshal doubles (from Paul Felix)
41     # 2002-03-17 fl Avoid buffered read when possible (from James Rucker)
42     # 2002-04-07 fl Added pythondoc comments
43     # 2002-04-16 fl Added __str__ methods to datetime/binary wrappers
44     # 2002-05-15 fl Added error constants (from Andrew Kuchling)
45     # 2002-06-27 fl Merged with Python CVS version
46     #
47     # Copyright (c) 1999-2002 by Secret Labs AB.
48     # Copyright (c) 1999-2002 by Fredrik Lundh.
49     #
50     # info@pythonware.com
51     # http://www.pythonware.com
52     #
53     # --------------------------------------------------------------------
54     # The XML-RPC client interface is
55     #
56     # Copyright (c) 1999-2002 by Secret Labs AB
57     # Copyright (c) 1999-2002 by Fredrik Lundh
58     #
59     # By obtaining, using, and/or copying this software and/or its
60     # associated documentation, you agree that you have read, understood,
61     # and will comply with the following terms and conditions:
62     #
63     # Permission to use, copy, modify, and distribute this software and
64     # its associated documentation for any purpose and without fee is
65     # hereby granted, provided that the above copyright notice appears in
66     # all copies, and that both that copyright notice and this permission
67     # notice appear in supporting documentation, and that the name of
68     # Secret Labs AB or the author not be used in advertising or publicity
69     # pertaining to distribution of the software without specific, written
70     # prior permission.
71     #
72     # SECRET LABS AB AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
73     # TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANT-
74     # ABILITY AND FITNESS. IN NO EVENT SHALL SECRET LABS AB OR THE AUTHOR
75     # BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
76     # DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
77     # WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
78     # ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
79     # OF THIS SOFTWARE.
80     # --------------------------------------------------------------------
81    
82     #
83     # things to look into some day:
84    
85     # TODO: sort out True/False/boolean issues for Python 2.3
86    
87     """
88     An XML-RPC client interface for Python.
89    
90     The marshalling and response parser code can also be used to
91     implement XML-RPC servers.
92    
93     Exported exceptions:
94    
95     Error Base class for client errors
96     ProtocolError Indicates an HTTP protocol error
97     ResponseError Indicates a broken response package
98     Fault Indicates an XML-RPC fault package
99    
100     Exported classes:
101    
102     ServerProxy Represents a logical connection to an XML-RPC server
103    
104     Boolean boolean wrapper to generate a "boolean" XML-RPC value
105     DateTime dateTime wrapper for an ISO 8601 string or time tuple or
106     localtime integer value to generate a "dateTime.iso8601"
107     XML-RPC value
108     Binary binary data wrapper
109    
110     SlowParser Slow but safe standard parser (based on xmllib)
111     Marshaller Generate an XML-RPC params chunk from a Python data structure
112     Unmarshaller Unmarshal an XML-RPC response from incoming XML event message
113     Transport Handles an HTTP transaction to an XML-RPC server
114     SafeTransport Handles an HTTPS transaction to an XML-RPC server
115    
116     Exported constants:
117    
118     True
119     False
120    
121     Exported functions:
122    
123     boolean Convert any Python value to an XML-RPC boolean
124     getparser Create instance of the fastest available parser & attach
125     to an unmarshalling object
126     dumps Convert an argument tuple or a Fault instance to an XML-RPC
127     request (or response, if the methodresponse option is used).
128     loads Convert an XML-RPC packet to unmarshalled data plus a method
129     name (None if not present).
130     """
131    
132     import re, string, time, operator
133    
134     from types import *
135    
136     # --------------------------------------------------------------------
137     # Internal stuff
138    
139     try:
140     unicode
141     except NameError:
142     unicode = None # unicode support not available
143    
144     def _decode(data, encoding, is8bit=re.compile("[\x80-\xff]").search):
145     # decode non-ascii string (if possible)
146     if unicode and encoding and is8bit(data):
147     data = unicode(data, encoding)
148     return data
149    
150     def escape(s, replace=string.replace):
151     s = replace(s, "&", "&")
152     s = replace(s, "<", "&lt;")
153     return replace(s, ">", "&gt;",)
154    
155     if unicode:
156     def _stringify(string):
157     # convert to 7-bit ascii if possible
158     try:
159     return str(string)
160     except UnicodeError:
161     return string
162     else:
163     def _stringify(string):
164     return string
165    
166     __version__ = "1.0.1"
167    
168     # xmlrpc integer limits
169     MAXINT = 2L**31-1
170     MININT = -2L**31
171    
172     # --------------------------------------------------------------------
173     # Error constants (from Dan Libby's specification at
174     # http://xmlrpc-epi.sourceforge.net/specs/rfc.fault_codes.php)
175    
176     # Ranges of errors
177     PARSE_ERROR = -32700
178     SERVER_ERROR = -32600
179     APPLICATION_ERROR = -32500
180     SYSTEM_ERROR = -32400
181     TRANSPORT_ERROR = -32300
182    
183     # Specific errors
184     NOT_WELLFORMED_ERROR = -32700
185     UNSUPPORTED_ENCODING = -32701
186     INVALID_ENCODING_CHAR = -32702
187     INVALID_XMLRPC = -32600
188     METHOD_NOT_FOUND = -32601
189     INVALID_METHOD_PARAMS = -32602
190     INTERNAL_ERROR = -32603
191    
192     # --------------------------------------------------------------------
193     # Exceptions
194    
195     ##
196     # Base class for all kinds of client-side errors.
197    
198     class Error(Exception):
199     """Base class for client errors."""
200     def __str__(self):
201     return repr(self)
202    
203     ##
204     # Indicates an HTTP-level protocol error. This is raised by the HTTP
205     # transport layer, if the server returns an error code other than 200
206     # (OK).
207     #
208     # @param url The target URL.
209     # @param errcode The HTTP error code.
210     # @param errmsg The HTTP error message.
211     # @param headers The HTTP header dictionary.
212    
213     class ProtocolError(Error):
214     """Indicates an HTTP protocol error."""
215     def __init__(self, url, errcode, errmsg, headers):
216     Error.__init__(self)
217     self.url = url
218     self.errcode = errcode
219     self.errmsg = errmsg
220     self.headers = headers
221     def __repr__(self):
222     return (
223     "<ProtocolError for %s: %s %s>" %
224     (self.url, self.errcode, self.errmsg)
225     )
226    
227     ##
228     # Indicates a broken XML-RPC response package. This exception is
229     # raised by the unmarshalling layer, if the XML-RPC response is
230     # malformed.
231    
232     class ResponseError(Error):
233     """Indicates a broken response package."""
234     pass
235    
236     ##
237     # Indicates an XML-RPC fault response package. This exception is
238     # raised by the unmarshalling layer, if the XML-RPC response contains
239     # a fault string. This exception can also used as a class, to
240     # generate a fault XML-RPC message.
241     #
242     # @param faultCode The XML-RPC fault code.
243     # @param faultString The XML-RPC fault string.
244    
245     class Fault(Error):
246     """Indicates an XML-RPC fault package."""
247     def __init__(self, faultCode, faultString, **extra):
248     Error.__init__(self)
249     self.faultCode = faultCode
250     self.faultString = faultString
251     def __repr__(self):
252     return (
253     "<Fault %s: %s>" %
254     (self.faultCode, repr(self.faultString))
255     )
256    
257     # --------------------------------------------------------------------
258     # Special values
259    
260     ##
261     # Wrapper for XML-RPC boolean values. Use the xmlrpclib.True and
262     # xmlrpclib.False constants, or the xmlrpclib.boolean() function, to
263     # generate boolean XML-RPC values.
264     #
265     # @param value A boolean value. Any true value is interpreted as True,
266     # all other values are interpreted as False.
267    
268     class Boolean:
269     """Boolean-value wrapper.
270    
271     Use True or False to generate a "boolean" XML-RPC value.
272     """
273    
274     def __init__(self, value = 0):
275     self.value = operator.truth(value)
276    
277     def encode(self, out):
278     out.write("<value><boolean>%d</boolean></value>\n" % self.value)
279    
280     def __cmp__(self, other):
281     if isinstance(other, Boolean):
282     other = other.value
283     return cmp(self.value, other)
284    
285     def __repr__(self):
286     if self.value:
287     return "<Boolean True at %x>" % id(self)
288     else:
289     return "<Boolean False at %x>" % id(self)
290    
291     def __int__(self):
292     return self.value
293    
294     def __nonzero__(self):
295     return self.value
296    
297     True, False = Boolean(1), Boolean(0)
298    
299     ##
300     # Map true or false value to XML-RPC boolean values.
301     #
302     # @def boolean(value)
303     # @param value A boolean value. Any true value is mapped to True,
304     # all other values are mapped to False.
305     # @return xmlrpclib.True or xmlrpclib.False.
306     # @see Boolean
307     # @see True
308     # @see False
309    
310     def boolean(value, _truefalse=(False, True)):
311     """Convert any Python value to XML-RPC 'boolean'."""
312     return _truefalse[operator.truth(value)]
313    
314     ##
315     # Wrapper for XML-RPC DateTime values. This converts a time value to
316     # the format used by XML-RPC.
317     # <p>
318     # The value can be given as a string in the format
319     # "yyyymmddThh:mm:ss", as a 9-item time tuple (as returned by
320     # time.localtime()), or an integer value (as returned by time.time()).
321     # The wrapper uses time.localtime() to convert an integer to a time
322     # tuple.
323     #
324     # @param value The time, given as an ISO 8601 string, a time
325     # tuple, or a integer time value.
326    
327     class DateTime:
328     """DateTime wrapper for an ISO 8601 string or time tuple or
329     localtime integer value to generate 'dateTime.iso8601' XML-RPC
330     value.
331     """
332    
333     def __init__(self, value=0):
334     if not isinstance(value, StringType):
335     if not isinstance(value, TupleType):
336     if value == 0:
337     value = time.time()
338     value = time.localtime(value)
339     value = time.strftime("%Y%m%dT%H:%M:%S", value)
340     self.value = value
341    
342     def __cmp__(self, other):
343     if isinstance(other, DateTime):
344     other = other.value
345     return cmp(self.value, other)
346    
347     ##
348     # Get date/time value.
349     #
350     # @return Date/time value, as an ISO 8601 string.
351    
352     def __str__(self):
353     return self.value
354    
355     def __repr__(self):
356     return "<DateTime %s at %x>" % (repr(self.value), id(self))
357    
358     def decode(self, data):
359     self.value = string.strip(data)
360    
361     def encode(self, out):
362     out.write("<value><dateTime.iso8601>")
363     out.write(self.value)
364     out.write("</dateTime.iso8601></value>\n")
365    
366     def _datetime(data):
367     # decode xml element contents into a DateTime structure.
368     value = DateTime()
369     value.decode(data)
370     return value
371    
372     ##
373     # Wrapper for binary data. This can be used to transport any kind
374     # of binary data over XML-RPC, using BASE64 encoding.
375     #
376     # @param data An 8-bit string containing arbitrary data.
377    
378     class Binary:
379     """Wrapper for binary data."""
380    
381     def __init__(self, data=None):
382     self.data = data
383    
384     ##
385     # Get buffer contents.
386     #
387     # @return Buffer contents, as an 8-bit string.
388    
389     def __str__(self):
390     return self.data or ""
391    
392     def __cmp__(self, other):
393     if isinstance(other, Binary):
394     other = other.data
395     return cmp(self.data, other)
396    
397     def decode(self, data):
398     import base64
399     self.data = base64.decodestring(data)
400    
401     def encode(self, out):
402     import base64, StringIO
403     out.write("<value><base64>\n")
404     base64.encode(StringIO.StringIO(self.data), out)
405     out.write("</base64></value>\n")
406    
407     def _binary(data):
408     # decode xml element contents into a Binary structure
409     value = Binary()
410     value.decode(data)
411     return value
412    
413     WRAPPERS = DateTime, Binary, Boolean
414    
415     # --------------------------------------------------------------------
416     # XML parsers
417    
418     try:
419     # optional xmlrpclib accelerator. for more information on this
420     # component, contact info@pythonware.com
421     import _xmlrpclib
422     FastParser = _xmlrpclib.Parser
423     FastUnmarshaller = _xmlrpclib.Unmarshaller
424     except (AttributeError, ImportError):
425     FastParser = FastUnmarshaller = None
426    
427     try:
428     import _xmlrpclib
429     FastMarshaller = _xmlrpclib.Marshaller
430     except (AttributeError, ImportError):
431     FastMarshaller = None
432    
433     #
434     # the SGMLOP parser is about 15x faster than Python's builtin
435     # XML parser. SGMLOP sources can be downloaded from:
436     #
437     # http://www.pythonware.com/products/xml/sgmlop.htm
438     #
439    
440     try:
441     import sgmlop
442     if not hasattr(sgmlop, "XMLParser"):
443     raise ImportError
444     except ImportError:
445     SgmlopParser = None # sgmlop accelerator not available
446     else:
447     class SgmlopParser:
448     def __init__(self, target):
449    
450     # setup callbacks
451     self.finish_starttag = target.start
452     self.finish_endtag = target.end
453     self.handle_data = target.data
454     self.handle_xml = target.xml
455    
456     # activate parser
457     self.parser = sgmlop.XMLParser()
458     self.parser.register(self)
459     self.feed = self.parser.feed
460     self.entity = {
461     "amp": "&", "gt": ">", "lt": "<",
462     "apos": "'", "quot": '"'
463     }
464    
465     def close(self):
466     try:
467     self.parser.close()
468     finally:
469     self.parser = self.feed = None # nuke circular reference
470    
471     def handle_proc(self, tag, attr):
472     m = re.search("encoding\s*=\s*['\"]([^\"']+)[\"']", attr)
473     if m:
474     self.handle_xml(m.group(1), 1)
475    
476     def handle_entityref(self, entity):
477     # <string> entity
478     try:
479     self.handle_data(self.entity[entity])
480     except KeyError:
481     self.handle_data("&%s;" % entity)
482    
483     try:
484     from xml.parsers import expat
485     if not hasattr(expat, "ParserCreate"):
486     raise ImportError
487     except ImportError:
488     ExpatParser = None # expat not available
489     else:
490     class ExpatParser:
491     # fast expat parser for Python 2.0 and later. this is about
492     # 50% slower than sgmlop, on roundtrip testing
493     def __init__(self, target):
494     self._parser = parser = expat.ParserCreate(None, None)
495     self._target = target
496     parser.StartElementHandler = target.start
497     parser.EndElementHandler = target.end
498     parser.CharacterDataHandler = target.data
499     encoding = None
500     if not parser.returns_unicode:
501     encoding = "utf-8"
502     target.xml(encoding, None)
503    
504     def feed(self, data):
505     self._parser.Parse(data, 0)
506    
507     def close(self):
508     self._parser.Parse("", 1) # end of data
509     del self._target, self._parser # get rid of circular references
510    
511     class SlowParser:
512     """Default XML parser (based on xmllib.XMLParser)."""
513     # this is about 10 times slower than sgmlop, on roundtrip
514     # testing.
515     def __init__(self, target):
516     import xmllib # lazy subclassing (!)
517     if xmllib.XMLParser not in SlowParser.__bases__:
518     SlowParser.__bases__ = (xmllib.XMLParser,)
519     self.handle_xml = target.xml
520     self.unknown_starttag = target.start
521     self.handle_data = target.data
522     self.handle_cdata = target.data
523     self.unknown_endtag = target.end
524     try:
525     xmllib.XMLParser.__init__(self, accept_utf8=1)
526     except TypeError:
527     xmllib.XMLParser.__init__(self) # pre-2.0
528    
529     # --------------------------------------------------------------------
530     # XML-RPC marshalling and unmarshalling code
531    
532     ##
533     # XML-RPC marshaller.
534     #
535     # @param encoding Default encoding for 8-bit strings. The default
536     # value is None (interpreted as UTF-8).
537     # @see dumps
538    
539     class Marshaller:
540     """Generate an XML-RPC params chunk from a Python data structure.
541    
542     Create a Marshaller instance for each set of parameters, and use
543     the "dumps" method to convert your data (represented as a tuple)
544     to an XML-RPC params chunk. To write a fault response, pass a
545     Fault instance instead. You may prefer to use the "dumps" module
546     function for this purpose.
547     """
548    
549     # by the way, if you don't understand what's going on in here,
550     # that's perfectly ok.
551    
552     def __init__(self, encoding=None):
553     self.memo = {}
554     self.data = None
555     self.encoding = encoding
556    
557     dispatch = {}
558    
559     def dumps(self, values):
560     out = []
561     write = out.append
562     dump = self.__dump
563     if isinstance(values, Fault):
564     # fault instance
565     write("<fault>\n")
566     dump(vars(values), write)
567     write("</fault>\n")
568     else:
569     # parameter block
570     # FIXME: the xml-rpc specification allows us to leave out
571     # the entire <params> block if there are no parameters.
572     # however, changing this may break older code (including
573     # old versions of xmlrpclib.py), so this is better left as
574     # is for now. See @XMLRPC3 for more information. /F
575     write("<params>\n")
576     for v in values:
577     write("<param>\n")
578     dump(v, write)
579     write("</param>\n")
580     write("</params>\n")
581     result = string.join(out, "")
582     return result
583    
584     def __dump(self, value, write):
585     try:
586     f = self.dispatch[type(value)]
587     except KeyError:
588     raise TypeError, "cannot marshal %s objects" % type(value)
589     else:
590     f(self, value, write)
591    
592     def dump_int(self, value, write):
593     # in case ints are > 32 bits
594     if value > MAXINT or value < MININT:
595     raise OverflowError, "int exceeds XML-RPC limits"
596     write("<value><int>")
597     write(str(value))
598     write("</int></value>\n")
599     dispatch[IntType] = dump_int
600    
601     def dump_long(self, value, write):
602     if value > MAXINT or value < MININT:
603     raise OverflowError, "long int exceeds XML-RPC limits"
604     write("<value><int>")
605     write(str(int(value)))
606     write("</int></value>\n")
607     dispatch[LongType] = dump_long
608    
609     def dump_double(self, value, write):
610     write("<value><double>")
611     write(repr(value))
612     write("</double></value>\n")
613     dispatch[FloatType] = dump_double
614    
615     def dump_string(self, value, write, escape=escape):
616     write("<value><string>")
617     write(escape(value))
618     write("</string></value>\n")
619     dispatch[StringType] = dump_string
620    
621     if unicode:
622     def dump_unicode(self, value, write, escape=escape):
623     value = value.encode(self.encoding)
624     write("<value><string>")
625     write(escape(value))
626     write("</string></value>\n")
627     dispatch[UnicodeType] = dump_unicode
628    
629     def dump_array(self, value, write):
630     i = id(value)
631     if self.memo.has_key(i):
632     raise TypeError, "cannot marshal recursive sequences"
633     self.memo[i] = None
634     dump = self.__dump
635     write("<value><array><data>\n")
636     for v in value:
637     dump(v, write)
638     write("</data></array></value>\n")
639     del self.memo[i]
640     dispatch[TupleType] = dump_array
641     dispatch[ListType] = dump_array
642    
643     def dump_struct(self, value, write, escape=escape):
644     i = id(value)
645     if self.memo.has_key(i):
646     raise TypeError, "cannot marshal recursive dictionaries"
647     self.memo[i] = None
648     dump = self.__dump
649     write("<value><struct>\n")
650     for k in value.keys():
651     write("<member>\n")
652     if type(k) is not StringType:
653     raise TypeError, "dictionary key must be string"
654     write("<name>%s</name>\n" % escape(k))
655     dump(value[k], write)
656     write("</member>\n")
657     write("</struct></value>\n")
658     del self.memo[i]
659     dispatch[DictType] = dump_struct
660    
661     def dump_instance(self, value, write):
662     # check for special wrappers
663     if value.__class__ in WRAPPERS:
664     self.write = write
665     value.encode(self)
666     del self.write
667     else:
668     # store instance attributes as a struct (really?)
669     self.dump_struct(value.__dict__, write)
670     dispatch[InstanceType] = dump_instance
671    
672     ##
673     # XML-RPC unmarshaller.
674     #
675     # @see loads
676    
677     class Unmarshaller:
678     """Unmarshal an XML-RPC response, based on incoming XML event
679     messages (start, data, end). Call close() to get the resulting
680     data structure.
681    
682     Note that this reader is fairly tolerant, and gladly accepts bogus
683     XML-RPC data without complaining (but not bogus XML).
684     """
685    
686     # and again, if you don't understand what's going on in here,
687     # that's perfectly ok.
688    
689     def __init__(self):
690     self._type = None
691     self._stack = []
692     self._marks = []
693     self._data = []
694     self._methodname = None
695     self._encoding = "utf-8"
696     self.append = self._stack.append
697    
698     def close(self):
699     # return response tuple and target method
700     if self._type is None or self._marks:
701     raise ResponseError()
702     if self._type == "fault":
703     raise apply(Fault, (), self._stack[0])
704     return tuple(self._stack)
705    
706     def getmethodname(self):
707     return self._methodname
708    
709     #
710     # event handlers
711    
712     def xml(self, encoding, standalone):
713     self._encoding = encoding
714     # FIXME: assert standalone == 1 ???
715    
716     def start(self, tag, attrs):
717     # prepare to handle this element
718     if tag == "array" or tag == "struct":
719     self._marks.append(len(self._stack))
720     self._data = []
721     self._value = (tag == "value")
722    
723     def data(self, text):
724     self._data.append(text)
725    
726     def end(self, tag, join=string.join):
727     # call the appropriate end tag handler
728     try:
729     f = self.dispatch[tag]
730     except KeyError:
731     pass # unknown tag ?
732     else:
733     return f(self, join(self._data, ""))
734    
735     #
736     # accelerator support
737    
738     def end_dispatch(self, tag, data):
739     # dispatch data
740     try:
741     f = self.dispatch[tag]
742     except KeyError:
743     pass # unknown tag ?
744     else:
745     return f(self, data)
746    
747     #
748     # element decoders
749    
750     dispatch = {}
751    
752     def end_boolean(self, data):
753     if data == "0":
754     self.append(False)
755     elif data == "1":
756     self.append(True)
757     else:
758     raise TypeError, "bad boolean value"
759     self._value = 0
760     dispatch["boolean"] = end_boolean
761    
762     def end_int(self, data):
763     self.append(int(data))
764     self._value = 0
765     dispatch["i4"] = end_int
766     dispatch["int"] = end_int
767    
768     def end_double(self, data):
769     self.append(float(data))
770     self._value = 0
771     dispatch["double"] = end_double
772    
773     def end_string(self, data):
774     if self._encoding:
775     data = _decode(data, self._encoding)
776     self.append(_stringify(data))
777     self._value = 0
778     dispatch["string"] = end_string
779     dispatch["name"] = end_string # struct keys are always strings
780    
781     def end_array(self, data):
782     mark = self._marks[-1]
783     del self._marks[-1]
784     # map arrays to Python lists
785     self._stack[mark:] = [self._stack[mark:]]
786     self._value = 0
787     dispatch["array"] = end_array
788    
789     def end_struct(self, data):
790     mark = self._marks[-1]
791     del self._marks[-1]
792     # map structs to Python dictionaries
793     dict = {}
794     items = self._stack[mark:]
795     for i in range(0, len(items), 2):
796     dict[_stringify(items[i])] = items[i+1]
797     self._stack[mark:] = [dict]
798     self._value = 0
799     dispatch["struct"] = end_struct
800    
801     def end_base64(self, data):
802     value = Binary()
803     value.decode(data)
804     self.append(value)
805     self._value = 0
806     dispatch["base64"] = end_base64
807    
808     def end_dateTime(self, data):
809     value = DateTime()
810     value.decode(data)
811     self.append(value)
812     dispatch["dateTime.iso8601"] = end_dateTime
813    
814     def end_value(self, data):
815     # if we stumble upon a value element with no internal
816     # elements, treat it as a string element
817     if self._value:
818     self.end_string(data)
819     dispatch["value"] = end_value
820    
821     def end_params(self, data):
822     self._type = "params"
823     dispatch["params"] = end_params
824    
825     def end_fault(self, data):
826     self._type = "fault"
827     dispatch["fault"] = end_fault
828    
829     def end_methodName(self, data):
830     if self._encoding:
831     data = _decode(data, self._encoding)
832     self._methodname = data
833     self._type = "methodName" # no params
834     dispatch["methodName"] = end_methodName
835    
836    
837     # --------------------------------------------------------------------
838     # convenience functions
839    
840     ##
841     # Create a parser object, and connect it to an unmarshalling instance.
842     # This function picks the fastest available XML parser.
843     #
844     # return A (parser, unmarshaller) tuple.
845    
846     def getparser():
847     """getparser() -> parser, unmarshaller
848    
849     Create an instance of the fastest available parser, and attach it
850     to an unmarshalling object. Return both objects.
851     """
852     if FastParser and FastUnmarshaller:
853     target = FastUnmarshaller(True, False, _binary, _datetime, Fault)
854     parser = FastParser(target)
855     else:
856     target = Unmarshaller()
857     if FastParser:
858     parser = FastParser(target)
859     elif SgmlopParser:
860     parser = SgmlopParser(target)
861     elif ExpatParser:
862     parser = ExpatParser(target)
863     else:
864     parser = SlowParser(target)
865     return parser, target
866    
867     ##
868     # Convert a Python tuple or a Fault instance to an XML-RPC packet.
869     #
870     # @def dumps(params, **options)
871     # @param params A tuple or Fault instance.
872     # @keyparam methodname If given, create a methodCall request for
873     # this method name.
874     # @keyparam methodresponse If given, create a methodResponse packet.
875     # If used with a tuple, the tuple must be a singleton (that is,
876     # it must contain exactly one element).
877     # @keyparam encoding The packet encoding.
878     # @return A string containing marshalled data.
879    
880     def dumps(params, methodname=None, methodresponse=None, encoding=None):
881     """data [,options] -> marshalled data
882    
883     Convert an argument tuple or a Fault instance to an XML-RPC
884     request (or response, if the methodresponse option is used).
885    
886     In addition to the data object, the following options can be given
887     as keyword arguments:
888    
889     methodname: the method name for a methodCall packet
890    
891     methodresponse: true to create a methodResponse packet.
892     If this option is used with a tuple, the tuple must be
893     a singleton (i.e. it can contain only one element).
894    
895     encoding: the packet encoding (default is UTF-8)
896    
897     All 8-bit strings in the data structure are assumed to use the
898     packet encoding. Unicode strings are automatically converted,
899     where necessary.
900     """
901    
902     assert isinstance(params, TupleType) or isinstance(params, Fault),\
903     "argument must be tuple or Fault instance"
904    
905     if isinstance(params, Fault):
906     methodresponse = 1
907     elif methodresponse and isinstance(params, TupleType):
908     assert len(params) == 1, "response tuple must be a singleton"
909    
910     if not encoding:
911     encoding = "utf-8"
912    
913     if FastMarshaller:
914     m = FastMarshaller(encoding)
915     else:
916     m = Marshaller(encoding)
917    
918     data = m.dumps(params)
919    
920     if encoding != "utf-8":
921     xmlheader = "<?xml version='1.0' encoding='%s'?>\n" % str(encoding)
922     else:
923     xmlheader = "<?xml version='1.0'?>\n" # utf-8 is default
924    
925     # standard XML-RPC wrappings
926     if methodname:
927     # a method call
928     if not isinstance(methodname, StringType):
929     methodname = methodname.encode(encoding)
930     data = (
931     xmlheader,
932     "<methodCall>\n"
933     "<methodName>", methodname, "</methodName>\n",
934     data,
935     "</methodCall>\n"
936     )
937     elif methodresponse:
938     # a method response, or a fault structure
939     data = (
940     xmlheader,
941     "<methodResponse>\n",
942     data,
943     "</methodResponse>\n"
944     )
945     else:
946     return data # return as is
947     return string.join(data, "")
948    
949     ##
950     # Convert an XML-RPC packet to a Python object. If the XML-RPC packet
951     # represents a fault condition, this function raises a Fault exception.
952     #
953     # @param data An XML-RPC packet, given as an 8-bit string.
954     # @return A tuple containing the the unpacked data, and the method name
955     # (None if not present).
956     # @see Fault
957    
958     def loads(data):
959     """data -> unmarshalled data, method name
960    
961     Convert an XML-RPC packet to unmarshalled data plus a method
962     name (None if not present).
963    
964     If the XML-RPC packet represents a fault condition, this function
965     raises a Fault exception.
966     """
967     import sys
968     p, u = getparser()
969     p.feed(data)
970     p.close()
971     return u.close(), u.getmethodname()
972    
973    
974     # --------------------------------------------------------------------
975     # request dispatcher
976    
977     class _Method:
978     # some magic to bind an XML-RPC method to an RPC server.
979     # supports "nested" methods (e.g. examples.getStateName)
980     def __init__(self, send, name):
981     self.__send = send
982     self.__name = name
983     def __getattr__(self, name):
984     return _Method(self.__send, "%s.%s" % (self.__name, name))
985     def __call__(self, *args):
986 joko 1.4 return self.__send(self.__name, args)
987 joko 1.1
988     ##
989     # Standard transport class for XML-RPC over HTTP.
990     # <p>
991     # You can create custom transports by subclassing this method, and
992     # overriding selected methods.
993    
994     class Transport:
995     """Handles an HTTP transaction to an XML-RPC server."""
996    
997     # client identifier (may be overridden)
998     user_agent = "xmlrpclib.py/%s (by www.pythonware.com)" % __version__
999    
1000 joko 1.2 #def __init__(self):
1001     #import xmlrpclib
1002     #self.phpsessionid = ""
1003    
1004 joko 1.1 ##
1005     # Send a complete request, and parse the response.
1006     #
1007     # @param host Target host.
1008     # @param handler Target PRC handler.
1009     # @param request_body XML-RPC request body.
1010     # @param verbose Debugging flag.
1011     # @return Parsed response.
1012    
1013     def request(self, host, handler, request_body, verbose=0):
1014     # issue XML-RPC request
1015    
1016     h = self.make_connection(host)
1017     if verbose:
1018     h.set_debuglevel(1)
1019    
1020     self.send_request(h, handler, request_body)
1021     self.send_host(h, host)
1022 joko 1.2 self.send_php_sessionid(h)
1023 joko 1.1 self.send_user_agent(h)
1024     self.send_content(h, request_body)
1025    
1026     response = h.getresponse()
1027    
1028     if response.status != 200:
1029     raise ProtocolError(
1030     host + handler,
1031 joko 1.5 response.status, response.reason, {}
1032 joko 1.1 )
1033    
1034     self.verbose = verbose
1035    
1036     try:
1037     sock = h._conn.sock
1038     except AttributeError:
1039     sock = None
1040    
1041 joko 1.2 import __main__
1042     __main__.phpsessionid = response.getheader('Set-Cookie')
1043 joko 1.1
1044     #return self._parse_response(h.getfile(), sock)
1045     return self._parse_response(response, None)
1046    
1047     ##
1048     # Create parser.
1049     #
1050     # @return A 2-tuple containing a parser and a unmarshaller.
1051    
1052     def getparser(self):
1053     # get parser and unmarshaller
1054     return getparser()
1055    
1056     ##
1057     # Connect to server.
1058     #
1059     # @param host Target host.
1060     # @return A connection handle.
1061    
1062     def make_connection(self, host):
1063     # create a HTTP connection object from a host descriptor
1064     import httplib
1065     return httplib.HTTPConnection(host)
1066    
1067     ##
1068     # Send request header.
1069     #
1070     # @param connection Connection handle.
1071     # @param handler Target RPC handler.
1072     # @param request_body XML-RPC body.
1073    
1074     def send_request(self, connection, handler, request_body):
1075     connection.putrequest("POST", handler)
1076    
1077     ##
1078     # Send host name.
1079     #
1080     # @param connection Connection handle.
1081     # @param host Host name.
1082    
1083     def send_host(self, connection, host):
1084     connection.putheader("Host", host)
1085    
1086     ##
1087     # Send host name.
1088     #
1089     # @param connection Connection handle.
1090     # @param host Host name.
1091    
1092 joko 1.2 def send_php_sessionid(self, connection):
1093     import __main__
1094     connection.putheader("Cookie", str(__main__.phpsessionid))
1095 joko 1.1
1096    
1097     ##
1098     # Send user-agent identifier.
1099     #
1100     # @param connection Connection handle.
1101    
1102     def send_user_agent(self, connection):
1103     connection.putheader("User-Agent", self.user_agent)
1104    
1105     ##
1106     # Send request body.
1107     #
1108     # @param connection Connection handle.
1109     # @param request_body XML-RPC request body.
1110    
1111     def send_content(self, connection, request_body):
1112     connection.putheader("Content-Type", "text/xml")
1113     connection.putheader("Content-Length", str(len(request_body)))
1114     connection.endheaders()
1115     if request_body:
1116     connection.send(request_body)
1117    
1118     ##
1119     # Parse response.
1120     #
1121     # @param file Stream.
1122     # @return Response tuple and target method.
1123    
1124     def parse_response(self, file):
1125     # compatibility interface
1126     return self._parse_response(file, None)
1127    
1128     ##
1129     # Parse response (alternate interface). This is similar to the
1130     # parse_response method, but also provides direct access to the
1131     # underlying socket object (where available).
1132     #
1133     # @param file Stream.
1134     # @param sock Socket handle (or None, if the socket object
1135     # could not be accessed).
1136     # @return Response tuple and target method.
1137    
1138     def _parse_response(self, file, sock):
1139     # read response from input file/socket, and parse it
1140    
1141     p, u = self.getparser()
1142    
1143     while 1:
1144     if sock:
1145     response = sock.recv(1024)
1146     else:
1147     response = file.read(1024)
1148     if not response:
1149     break
1150     if self.verbose:
1151     print "body:", repr(response)
1152     p.feed(response)
1153    
1154     file.close()
1155     p.close()
1156    
1157     return u.close()
1158    
1159     ##
1160     # Standard transport class for XML-RPC over HTTPS.
1161    
1162     class SafeTransport(Transport):
1163     """Handles an HTTPS transaction to an XML-RPC server."""
1164    
1165     # FIXME: mostly untested
1166    
1167     def make_connection(self, host):
1168     # create a HTTPS connection object from a host descriptor
1169     # host may be a string, or a (host, x509-dict) tuple
1170     import httplib
1171     if isinstance(host, TupleType):
1172     host, x509 = host
1173     else:
1174     x509 = {}
1175     try:
1176     HTTPS = httplib.HTTPS
1177     except AttributeError:
1178     raise NotImplementedError,\
1179     "your version of httplib doesn't support HTTPS"
1180     else:
1181     return apply(HTTPS, (host, None), x509)
1182    
1183     def send_host(self, connection, host):
1184     if isinstance(host, TupleType):
1185     host, x509 = host
1186     connection.putheader("Host", host)
1187    
1188     ##
1189     # Standard server proxy. This class establishes a virtual connection
1190     # to an XML-RPC server.
1191     # <p>
1192     # This class is available as ServerProxy and Server. New code should
1193     # use ServerProxy, to avoid confusion.
1194     #
1195     # @def ServerProxy(uri, **options)
1196     # @param uri The connection point on the server.
1197     # @keyparam transport A transport factory, compatible with the
1198     # standard transport class.
1199     # @keyparam encoding The default encoding used for 8-bit strings
1200     # (default is UTF-8).
1201     # @keyparam verbose Use a true value to enable debugging output.
1202     # (printed to standard output).
1203     # @see Transport
1204    
1205     class ServerProxy:
1206     """uri [,options] -> a logical connection to an XML-RPC server
1207    
1208     uri is the connection point on the server, given as
1209     scheme://host/target.
1210    
1211     The standard implementation always supports the "http" scheme. If
1212     SSL socket support is available (Python 2.0), it also supports
1213     "https".
1214    
1215     If the target part and the slash preceding it are both omitted,
1216     "/RPC2" is assumed.
1217    
1218     The following options can be given as keyword arguments:
1219    
1220     transport: a transport factory
1221     encoding: the request encoding (default is UTF-8)
1222    
1223     All 8-bit strings passed to the server proxy are assumed to use
1224     the given encoding.
1225     """
1226    
1227     def __init__(self, uri, transport=None, encoding=None, verbose=0):
1228     # establish a "logical" server connection
1229    
1230     # get the url
1231     import urllib
1232     type, uri = urllib.splittype(uri)
1233     if type not in ("http", "https"):
1234     raise IOError, "unsupported XML-RPC protocol"
1235     self.__host, self.__handler = urllib.splithost(uri)
1236     if not self.__handler:
1237     self.__handler = "/RPC2"
1238    
1239     if transport is None:
1240     if type == "https":
1241     transport = SafeTransport()
1242     else:
1243     transport = Transport()
1244     self.__transport = transport
1245    
1246     self.__encoding = encoding
1247     self.__verbose = verbose
1248    
1249     def __request(self, methodname, params):
1250     # call a method on the remote server
1251    
1252     request = dumps(params, methodname, encoding=self.__encoding)
1253    
1254     response = self.__transport.request(
1255     self.__host,
1256     self.__handler,
1257     request,
1258     verbose=self.__verbose
1259     )
1260    
1261     if len(response) == 1:
1262     response = response[0]
1263    
1264     return response
1265    
1266     def __repr__(self):
1267     return (
1268     "<ServerProxy for %s%s>" %
1269     (self.__host, self.__handler)
1270     )
1271    
1272     __str__ = __repr__
1273    
1274     def __getattr__(self, name):
1275     # magic method dispatcher
1276     return _Method(self.__request, name)
1277    
1278     # note: to call a remote object with an non-standard name, use
1279     # result getattr(server, "strange-python-name")(args)
1280    
1281     # compatibility
1282    
1283     Server = ServerProxy
1284    
1285     # --------------------------------------------------------------------
1286     # test code
1287    
1288     if __name__ == "__main__":
1289    
1290     # simple test program (from the XML-RPC specification)
1291    
1292     # server = ServerProxy("http://localhost:8000") # local server
1293     server = ServerProxy("http://betty.userland.com")
1294    
1295     print server
1296    
1297     try:
1298     print server.examples.getStateName(41)
1299     except Error, v:
1300     print "ERROR", v

MailToCvsAdmin">MailToCvsAdmin
ViewVC Help
Powered by ViewVC 1.1.26 RSS 2.0 feed