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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.5 - (show annotations)
Tue Aug 31 16:55:34 2004 UTC (19 years, 10 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 #
2 # XML-RPC CLIENT LIBRARY
3 # $Id: xmlrpclib.py,v 1.4 2004/08/31 02:24:28 joko Exp $
4 #
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 return self.__send(self.__name, args)
987
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 #def __init__(self):
1001 #import xmlrpclib
1002 #self.phpsessionid = ""
1003
1004 ##
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 self.send_php_sessionid(h)
1023 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 response.status, response.reason, {}
1032 )
1033
1034 self.verbose = verbose
1035
1036 try:
1037 sock = h._conn.sock
1038 except AttributeError:
1039 sock = None
1040
1041 import __main__
1042 __main__.phpsessionid = response.getheader('Set-Cookie')
1043
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 def send_php_sessionid(self, connection):
1093 import __main__
1094 connection.putheader("Cookie", str(__main__.phpsessionid))
1095
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