Add timestamp offset for block header
[p2pool.git] / SOAPpy / Types.py
1 from __future__ import nested_scopes
2
3 """
4 ################################################################################
5 # Copyright (c) 2003, Pfizer
6 # Copyright (c) 2001, Cayce Ullman.
7 # Copyright (c) 2001, Brian Matthews.
8 #
9 # All rights reserved.
10 #
11 # Redistribution and use in source and binary forms, with or without
12 # modification, are permitted provided that the following conditions are met:
13 # Redistributions of source code must retain the above copyright notice, this
14 # list of conditions and the following disclaimer.
15 #
16 # Redistributions in binary form must reproduce the above copyright notice,
17 # this list of conditions and the following disclaimer in the documentation
18 # and/or other materials provided with the distribution.
19 #
20 # Neither the name of actzero, inc. nor the names of its contributors may
21 # be used to endorse or promote products derived from this software without
22 # specific prior written permission.
23 #
24 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 # ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR
28 # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #
35 ################################################################################
36 """
37
38 ident = '$Id: Types.py 1496 2010-03-04 23:46:17Z pooryorick $'
39 from version import __version__
40
41 import UserList
42 import base64
43 import cgi
44 import urllib
45 import copy
46 import re
47 import time
48 from types import *
49
50 # SOAPpy modules
51 from Errors    import *
52 from NS        import NS
53 from Utilities import encodeHexString, cleanDate
54 from Config    import Config
55
56 ###############################################################################
57 # Utility functions
58 ###############################################################################
59
60 def isPrivate(name): return name[0]=='_'
61 def isPublic(name):  return name[0]!='_'
62
63 ###############################################################################
64 # Types and Wrappers
65 ###############################################################################
66
67 class anyType:
68     _validURIs = (NS.XSD, NS.XSD2, NS.XSD3, NS.ENC)
69
70     def __init__(self, data = None, name = None, typed = 1, attrs = None):
71         if self.__class__ == anyType:
72             raise Error, "anyType can't be instantiated directly"
73
74         if type(name) in (ListType, TupleType):
75             self._ns, self._name = name
76         else:
77             self._ns = self._validURIs[0]
78             self._name = name
79             
80         self._typed = typed
81         self._attrs = {}
82
83         self._cache = None
84         self._type = self._typeName()
85
86         self._data = self._checkValueSpace(data)
87
88         if attrs != None:
89             self._setAttrs(attrs)
90
91     def __str__(self):
92         if hasattr(self,'_name') and self._name:
93             return "<%s %s at %d>" % (self.__class__, self._name, id(self))
94         return "<%s at %d>" % (self.__class__, id(self))
95
96     __repr__ = __str__
97
98     def _checkValueSpace(self, data):
99         return data
100
101     def _marshalData(self):
102         return str(self._data)
103
104     def _marshalAttrs(self, ns_map, builder):
105         a = ''
106
107         for attr, value in self._attrs.items():
108             ns, n = builder.genns(ns_map, attr[0])
109             a += n + ' %s%s="%s"' % \
110                 (ns, attr[1], cgi.escape(str(value), 1))
111
112         return a
113
114     def _fixAttr(self, attr):
115         if type(attr) in (StringType, UnicodeType):
116             attr = (None, attr)
117         elif type(attr) == ListType:
118             attr = tuple(attr)
119         elif type(attr) != TupleType:
120             raise AttributeError, "invalid attribute type"
121
122         if len(attr) != 2:
123             raise AttributeError, "invalid attribute length"
124
125         if type(attr[0]) not in (NoneType, StringType, UnicodeType):
126             raise AttributeError, "invalid attribute namespace URI type"
127
128         return attr
129
130     def _getAttr(self, attr):
131         attr = self._fixAttr(attr)
132
133         try:
134             return self._attrs[attr]
135         except:
136             return None
137
138     def _setAttr(self, attr, value):
139         attr = self._fixAttr(attr)
140
141         if type(value) is StringType:
142             value = unicode(value)
143
144         self._attrs[attr] = value
145             
146
147     def _setAttrs(self, attrs):
148         if type(attrs) in (ListType, TupleType):
149             for i in range(0, len(attrs), 2):
150                 self._setAttr(attrs[i], attrs[i + 1])
151
152             return
153
154         if type(attrs) == DictType:
155             d = attrs
156         elif isinstance(attrs, anyType):
157             d = attrs._attrs
158         else:
159             raise AttributeError, "invalid attribute type"
160
161         for attr, value in d.items():
162             self._setAttr(attr, value)
163
164     def _setMustUnderstand(self, val):
165         self._setAttr((NS.ENV, "mustUnderstand"), val)
166
167     def _getMustUnderstand(self):
168         return self._getAttr((NS.ENV, "mustUnderstand"))
169
170     def _setActor(self, val):
171         self._setAttr((NS.ENV, "actor"), val)
172
173     def _getActor(self):
174         return self._getAttr((NS.ENV, "actor"))
175
176     def _typeName(self):
177         return self.__class__.__name__[:-4]
178
179     def _validNamespaceURI(self, URI, strict):
180         if not hasattr(self, '_typed') or not self._typed:
181             return None
182         if URI in self._validURIs:
183             return URI
184         if not strict:
185             return self._ns
186         raise AttributeError, \
187             "not a valid namespace for type %s" % self._type
188
189 class voidType(anyType):
190     pass
191
192 class stringType(anyType):
193     def _checkValueSpace(self, data):
194         if data == None:
195             raise ValueError, "must supply initial %s value" % self._type
196
197         if type(data) not in (StringType, UnicodeType):
198             raise AttributeError, "invalid %s type:" % self._type
199
200         return data
201
202     def _marshalData(self):
203         return self._data
204
205
206 class untypedType(stringType):
207     def __init__(self, data = None, name = None, attrs = None):
208         stringType.__init__(self, data, name, 0, attrs)
209
210 class IDType(stringType): pass
211 class NCNameType(stringType): pass
212 class NameType(stringType): pass
213 class ENTITYType(stringType): pass
214 class IDREFType(stringType): pass
215 class languageType(stringType): pass
216 class NMTOKENType(stringType): pass
217 class QNameType(stringType): pass
218
219 class tokenType(anyType):
220     _validURIs = (NS.XSD2, NS.XSD3)
221     __invalidre = '[\n\t]|^ | $|  '
222
223     def _checkValueSpace(self, data):
224         if data == None:
225             raise ValueError, "must supply initial %s value" % self._type
226
227         if type(data) not in (StringType, UnicodeType):
228             raise AttributeError, "invalid %s type" % self._type
229
230         if type(self.__invalidre) == StringType:
231             self.__invalidre = re.compile(self.__invalidre)
232
233             if self.__invalidre.search(data):
234                 raise ValueError, "invalid %s value" % self._type
235
236         return data
237
238 class normalizedStringType(anyType):
239     _validURIs = (NS.XSD3,)
240     __invalidre = '[\n\r\t]'
241
242     def _checkValueSpace(self, data):
243         if data == None:
244             raise ValueError, "must supply initial %s value" % self._type
245
246         if type(data) not in (StringType, UnicodeType):
247             raise AttributeError, "invalid %s type" % self._type
248
249         if type(self.__invalidre) == StringType:
250             self.__invalidre = re.compile(self.__invalidre)
251
252             if self.__invalidre.search(data):
253                 raise ValueError, "invalid %s value" % self._type
254
255         return data
256
257 class CDATAType(normalizedStringType):
258     _validURIs = (NS.XSD2,)
259
260 class booleanType(anyType):
261     def __int__(self):
262         return self._data
263
264     __nonzero__ = __int__
265
266     def _marshalData(self):
267         return ['false', 'true'][self._data]
268
269     def _checkValueSpace(self, data):
270         if data == None:
271             raise ValueError, "must supply initial %s value" % self._type
272
273         if data in (0, '0', 'false', ''):
274             return 0
275         if data in (1, '1', 'true'):
276             return 1
277         raise ValueError, "invalid %s value" % self._type
278
279 class decimalType(anyType):
280     def _checkValueSpace(self, data):
281         if data == None:
282             raise ValueError, "must supply initial %s value" % self._type
283
284         if type(data) not in (IntType, LongType, FloatType):
285             raise Error, "invalid %s value" % self._type
286
287         return data
288
289 class floatType(anyType):
290     def _checkValueSpace(self, data):
291         if data == None:
292             raise ValueError, "must supply initial %s value" % self._type
293
294         if type(data) not in (IntType, LongType, FloatType) or \
295             data < -3.4028234663852886E+38 or \
296             data >  3.4028234663852886E+38:
297             raise ValueError, "invalid %s value: %s" % (self._type, repr(data))
298
299         return data
300
301     def _marshalData(self):
302         return "%.18g" % self._data # More precision
303
304 class doubleType(anyType):
305     def _checkValueSpace(self, data):
306         if data == None:
307             raise ValueError, "must supply initial %s value" % self._type
308
309         if type(data) not in (IntType, LongType, FloatType) or \
310             data < -1.7976931348623158E+308 or \
311             data  > 1.7976931348623157E+308:
312             raise ValueError, "invalid %s value: %s" % (self._type, repr(data))
313
314         return data
315
316     def _marshalData(self):
317         return "%.18g" % self._data # More precision
318
319 class durationType(anyType):
320     _validURIs = (NS.XSD3,)
321
322     def _checkValueSpace(self, data):
323         if data == None:
324             raise ValueError, "must supply initial %s value" % self._type
325
326         try:
327             # A tuple or a scalar is OK, but make them into a list
328
329             if type(data) == TupleType:
330                 data = list(data)
331             elif type(data) != ListType:
332                 data = [data]
333
334             if len(data) > 6:
335                 raise Exception, "too many values"
336
337             # Now check the types of all the components, and find
338             # the first nonzero element along the way.
339
340             f = -1
341
342             for i in range(len(data)):
343                 if data[i] == None:
344                     data[i] = 0
345                     continue
346
347                 if type(data[i]) not in \
348                     (IntType, LongType, FloatType):
349                     raise Exception, "element %d a bad type" % i
350
351                 if data[i] and f == -1:
352                     f = i
353
354             # If they're all 0, just use zero seconds.
355
356             if f == -1:
357                 self._cache = 'PT0S'
358
359                 return (0,) * 6
360
361             # Make sure only the last nonzero element has a decimal fraction
362             # and only the first element is negative.
363
364             d = -1
365
366             for i in range(f, len(data)):
367                 if data[i]:
368                     if d != -1:
369                         raise Exception, \
370                             "all except the last nonzero element must be " \
371                             "integers"
372                     if data[i] < 0 and i > f:
373                         raise Exception, \
374                             "only the first nonzero element can be negative"
375                     elif data[i] != long(data[i]):
376                         d = i
377
378             # Pad the list on the left if necessary.
379
380             if len(data) < 6:
381                 n = 6 - len(data)
382                 f += n
383                 d += n
384                 data = [0] * n + data
385
386             # Save index of the first nonzero element and the decimal
387             # element for _marshalData.
388
389             self.__firstnonzero = f
390             self.__decimal = d
391
392         except Exception, e:
393             raise ValueError, "invalid %s value - %s" % (self._type, e)
394
395         return tuple(data)
396
397     def _marshalData(self):
398         if self._cache == None:
399             d = self._data
400             t = 0
401
402             if d[self.__firstnonzero] < 0:
403                 s = '-P'
404             else:
405                 s = 'P'
406
407             t = 0
408
409             for i in range(self.__firstnonzero, len(d)):
410                 if d[i]:
411                     if i > 2 and not t:
412                         s += 'T'
413                         t = 1
414                     if self.__decimal == i:
415                         s += "%g" % abs(d[i])
416                     else:
417                         s += "%d" % long(abs(d[i]))
418                     s += ['Y', 'M', 'D', 'H', 'M', 'S'][i]
419
420             self._cache = s
421
422         return self._cache
423
424 class timeDurationType(durationType):
425     _validURIs = (NS.XSD, NS.XSD2, NS.ENC)
426
427 class dateTimeType(anyType):
428     _validURIs = (NS.XSD3,)
429
430     def _checkValueSpace(self, data):
431         try:
432             if data == None:
433                 data = time.time()
434
435             if (type(data) in (IntType, LongType)):
436                 data = list(time.gmtime(data)[:6])
437             elif (type(data) == FloatType):
438                 f = data - int(data)
439                 data = list(time.gmtime(int(data))[:6])
440                 data[5] += f
441             elif type(data) in (ListType, TupleType):
442                 if len(data) < 6:
443                     raise Exception, "not enough values"
444                 if len(data) > 9:
445                     raise Exception, "too many values"
446
447                 data = list(data[:6])
448
449                 cleanDate(data)
450             else:
451                 raise Exception, "invalid type"
452         except Exception, e:
453             raise ValueError, "invalid %s value - %s" % (self._type, e)
454
455         return tuple(data)
456
457     def _marshalData(self):
458         if self._cache == None:
459             d = self._data
460             s = "%04d-%02d-%02dT%02d:%02d:%02d" % ((abs(d[0]),) + d[1:])
461             if d[0] < 0:
462                 s = '-' + s
463             f = d[5] - int(d[5])
464             if f != 0:
465                 s += ("%g" % f)[1:]
466             s += 'Z'
467
468             self._cache = s
469
470         return self._cache
471
472 class recurringInstantType(anyType):
473     _validURIs = (NS.XSD,)
474
475     def _checkValueSpace(self, data):
476         try:
477             if data == None:
478                 data = list(time.gmtime(time.time())[:6])
479             if (type(data) in (IntType, LongType)):
480                 data = list(time.gmtime(data)[:6])
481             elif (type(data) == FloatType):
482                 f = data - int(data)
483                 data = list(time.gmtime(int(data))[:6])
484                 data[5] += f
485             elif type(data) in (ListType, TupleType):
486                 if len(data) < 1:
487                     raise Exception, "not enough values"
488                 if len(data) > 9:
489                     raise Exception, "too many values"
490
491                 data = list(data[:6])
492
493                 if len(data) < 6:
494                     data += [0] * (6 - len(data))
495
496                 f = len(data)
497
498                 for i in range(f):
499                     if data[i] == None:
500                         if f < i:
501                             raise Exception, \
502                                 "only leftmost elements can be none"
503                     else:
504                         f = i
505                         break
506
507                 cleanDate(data, f)
508             else:
509                 raise Exception, "invalid type"
510         except Exception, e:
511             raise ValueError, "invalid %s value - %s" % (self._type, e)
512
513         return tuple(data)
514
515     def _marshalData(self):
516         if self._cache == None:
517             d = self._data
518             e = list(d)
519             neg = ''
520
521             if not e[0]:
522                 e[0] = '--'
523             else:
524                 if e[0] < 0:
525                     neg = '-'
526                     e[0] = abs(e[0])
527                 if e[0] < 100:
528                     e[0] = '-' + "%02d" % e[0]
529                 else:
530                     e[0] = "%04d" % e[0]
531
532             for i in range(1, len(e)):
533                 if e[i] == None or (i < 3 and e[i] == 0):
534                     e[i] = '-'
535                 else:
536                     if e[i] < 0:
537                         neg = '-'
538                         e[i] = abs(e[i])
539
540                     e[i] = "%02d" % e[i]
541
542             if d[5]:
543                 f = abs(d[5] - int(d[5]))
544
545                 if f:
546                     e[5] += ("%g" % f)[1:]
547
548             s = "%s%s-%s-%sT%s:%s:%sZ" % ((neg,) + tuple(e))
549
550             self._cache = s
551
552         return self._cache
553
554 class timeInstantType(dateTimeType):
555     _validURIs = (NS.XSD, NS.XSD2, NS.ENC)
556
557 class timePeriodType(dateTimeType):
558     _validURIs = (NS.XSD2, NS.ENC)
559
560 class timeType(anyType):
561     def _checkValueSpace(self, data):
562         try:
563             if data == None:
564                 data = time.gmtime(time.time())[3:6]
565             elif (type(data) == FloatType):
566                 f = data - int(data)
567                 data = list(time.gmtime(int(data))[3:6])
568                 data[2] += f
569             elif type(data) in (IntType, LongType):
570                 data = time.gmtime(data)[3:6]
571             elif type(data) in (ListType, TupleType):
572                 if len(data) == 9:
573                     data = data[3:6]
574                 elif len(data) > 3:
575                     raise Exception, "too many values"
576
577                 data = [None, None, None] + list(data)
578
579                 if len(data) < 6:
580                     data += [0] * (6 - len(data))
581
582                 cleanDate(data, 3)
583
584                 data = data[3:]
585             else:
586                 raise Exception, "invalid type"
587         except Exception, e:
588             raise ValueError, "invalid %s value - %s" % (self._type, e)
589
590         return tuple(data)
591
592     def _marshalData(self):
593         if self._cache == None:
594             d = self._data
595             #s = ''
596             #
597             #s = time.strftime("%H:%M:%S", (0, 0, 0) + d + (0, 0, -1))
598             s = "%02d:%02d:%02d" % d
599             f = d[2] - int(d[2])
600             if f != 0:
601                 s += ("%g" % f)[1:]
602             s += 'Z'
603
604             self._cache = s
605
606         return self._cache
607
608 class dateType(anyType):
609     def _checkValueSpace(self, data):
610         try:
611             if data == None:
612                 data = time.gmtime(time.time())[0:3]
613             elif type(data) in (IntType, LongType, FloatType):
614                 data = time.gmtime(data)[0:3]
615             elif type(data) in (ListType, TupleType):
616                 if len(data) == 9:
617                     data = data[0:3]
618                 elif len(data) > 3:
619                     raise Exception, "too many values"
620
621                 data = list(data)
622
623                 if len(data) < 3:
624                     data += [1, 1, 1][len(data):]
625
626                 data += [0, 0, 0]
627
628                 cleanDate(data)
629
630                 data = data[:3]
631             else:
632                 raise Exception, "invalid type"
633         except Exception, e:
634             raise ValueError, "invalid %s value - %s" % (self._type, e)
635
636         return tuple(data)
637
638     def _marshalData(self):
639         if self._cache == None:
640             d = self._data
641             s = "%04d-%02d-%02dZ" % ((abs(d[0]),) + d[1:])
642             if d[0] < 0:
643                 s = '-' + s
644
645             self._cache = s
646
647         return self._cache
648
649 class gYearMonthType(anyType):
650     _validURIs = (NS.XSD3,)
651
652     def _checkValueSpace(self, data):
653         try:
654             if data == None:
655                 data = time.gmtime(time.time())[0:2]
656             elif type(data) in (IntType, LongType, FloatType):
657                 data = time.gmtime(data)[0:2]
658             elif type(data) in (ListType, TupleType):
659                 if len(data) == 9:
660                     data = data[0:2]
661                 elif len(data) > 2:
662                     raise Exception, "too many values"
663
664                 data = list(data)
665
666                 if len(data) < 2:
667                     data += [1, 1][len(data):]
668
669                 data += [1, 0, 0, 0]
670
671                 cleanDate(data)
672
673                 data = data[:2]
674             else:
675                 raise Exception, "invalid type"
676         except Exception, e:
677             raise ValueError, "invalid %s value - %s" % (self._type, e)
678
679         return tuple(data)
680
681     def _marshalData(self):
682         if self._cache == None:
683             d = self._data
684             s = "%04d-%02dZ" % ((abs(d[0]),) + d[1:])
685             if d[0] < 0:
686                 s = '-' + s
687
688             self._cache = s
689
690         return self._cache
691
692 class gYearType(anyType):
693     _validURIs = (NS.XSD3,)
694
695     def _checkValueSpace(self, data):
696         try:
697             if data == None:
698                 data = time.gmtime(time.time())[0:1]
699             elif type(data) in (IntType, LongType, FloatType):
700                 data = [data]
701
702             if type(data) in (ListType, TupleType):
703                 if len(data) == 9:
704                     data = data[0:1]
705                 elif len(data) < 1:
706                     raise Exception, "too few values"
707                 elif len(data) > 1:
708                     raise Exception, "too many values"
709
710                 if type(data[0]) == FloatType:
711                     try: s = int(data[0])
712                     except: s = long(data[0])
713
714                     if s != data[0]:
715                         raise Exception, "not integral"
716
717                     data = [s]
718                 elif type(data[0]) not in (IntType, LongType):
719                     raise Exception, "bad type"
720             else:
721                 raise Exception, "invalid type"
722         except Exception, e:
723             raise ValueError, "invalid %s value - %s" % (self._type, e)
724
725         return data[0]
726
727     def _marshalData(self):
728         if self._cache == None:
729             d = self._data
730             s = "%04dZ" % abs(d)
731             if d < 0:
732                 s = '-' + s
733
734             self._cache = s
735
736         return self._cache
737
738 class centuryType(anyType):
739     _validURIs = (NS.XSD2, NS.ENC)
740
741     def _checkValueSpace(self, data):
742         try:
743             if data == None:
744                 data = time.gmtime(time.time())[0:1] / 100
745             elif type(data) in (IntType, LongType, FloatType):
746                 data = [data]
747
748             if type(data) in (ListType, TupleType):
749                 if len(data) == 9:
750                     data = data[0:1] / 100
751                 elif len(data) < 1:
752                     raise Exception, "too few values"
753                 elif len(data) > 1:
754                     raise Exception, "too many values"
755
756                 if type(data[0]) == FloatType:
757                     try: s = int(data[0])
758                     except: s = long(data[0])
759
760                     if s != data[0]:
761                         raise Exception, "not integral"
762
763                     data = [s]
764                 elif type(data[0]) not in (IntType, LongType):
765                     raise Exception, "bad type"
766             else:
767                 raise Exception, "invalid type"
768         except Exception, e:
769             raise ValueError, "invalid %s value - %s" % (self._type, e)
770
771         return data[0]
772
773     def _marshalData(self):
774         if self._cache == None:
775             d = self._data
776             s = "%02dZ" % abs(d)
777             if d < 0:
778                 s = '-' + s
779
780             self._cache = s
781
782         return self._cache
783
784 class yearType(gYearType):
785     _validURIs = (NS.XSD2, NS.ENC)
786
787 class gMonthDayType(anyType):
788     _validURIs = (NS.XSD3,)
789
790     def _checkValueSpace(self, data):
791         try:
792             if data == None:
793                 data = time.gmtime(time.time())[1:3]
794             elif type(data) in (IntType, LongType, FloatType):
795                 data = time.gmtime(data)[1:3]
796             elif type(data) in (ListType, TupleType):
797                 if len(data) == 9:
798                     data = data[0:2]
799                 elif len(data) > 2:
800                     raise Exception, "too many values"
801
802                 data = list(data)
803
804                 if len(data) < 2:
805                     data += [1, 1][len(data):]
806
807                 data = [0] + data + [0, 0, 0]
808
809                 cleanDate(data, 1)
810
811                 data = data[1:3]
812             else:
813                 raise Exception, "invalid type"
814         except Exception, e:
815             raise ValueError, "invalid %s value - %s" % (self._type, e)
816
817         return tuple(data)
818
819     def _marshalData(self):
820         if self._cache == None:
821             self._cache = "--%02d-%02dZ" % self._data
822
823         return self._cache
824
825 class recurringDateType(gMonthDayType):
826     _validURIs = (NS.XSD2, NS.ENC)
827
828 class gMonthType(anyType):
829     _validURIs = (NS.XSD3,)
830
831     def _checkValueSpace(self, data):
832         try:
833             if data == None:
834                 data = time.gmtime(time.time())[1:2]
835             elif type(data) in (IntType, LongType, FloatType):
836                 data = [data]
837
838             if type(data) in (ListType, TupleType):
839                 if len(data) == 9:
840                     data = data[1:2]
841                 elif len(data) < 1:
842                     raise Exception, "too few values"
843                 elif len(data) > 1:
844                     raise Exception, "too many values"
845
846                 if type(data[0]) == FloatType:
847                     try: s = int(data[0])
848                     except: s = long(data[0])
849
850                     if s != data[0]:
851                         raise Exception, "not integral"
852
853                     data = [s]
854                 elif type(data[0]) not in (IntType, LongType):
855                     raise Exception, "bad type"
856
857                 if data[0] < 1 or data[0] > 12:
858                     raise Exception, "bad value"
859             else:
860                 raise Exception, "invalid type"
861         except Exception, e:
862             raise ValueError, "invalid %s value - %s" % (self._type, e)
863
864         return data[0]
865
866     def _marshalData(self):
867         if self._cache == None:
868             self._cache = "--%02d--Z" % self._data
869
870         return self._cache
871
872 class monthType(gMonthType):
873     _validURIs = (NS.XSD2, NS.ENC)
874
875 class gDayType(anyType):
876     _validURIs = (NS.XSD3,)
877
878     def _checkValueSpace(self, data):
879         try:
880             if data == None:
881                 data = time.gmtime(time.time())[2:3]
882             elif type(data) in (IntType, LongType, FloatType):
883                 data = [data]
884
885             if type(data) in (ListType, TupleType):
886                 if len(data) == 9:
887                     data = data[2:3]
888                 elif len(data) < 1:
889                     raise Exception, "too few values"
890                 elif len(data) > 1:
891                     raise Exception, "too many values"
892
893                 if type(data[0]) == FloatType:
894                     try: s = int(data[0])
895                     except: s = long(data[0])
896
897                     if s != data[0]:
898                         raise Exception, "not integral"
899
900                     data = [s]
901                 elif type(data[0]) not in (IntType, LongType):
902                     raise Exception, "bad type"
903
904                 if data[0] < 1 or data[0] > 31:
905                     raise Exception, "bad value"
906             else:
907                 raise Exception, "invalid type"
908         except Exception, e:
909             raise ValueError, "invalid %s value - %s" % (self._type, e)
910
911         return data[0]
912
913     def _marshalData(self):
914         if self._cache == None:
915             self._cache = "---%02dZ" % self._data
916
917         return self._cache
918
919 class recurringDayType(gDayType):
920     _validURIs = (NS.XSD2, NS.ENC)
921
922 class hexBinaryType(anyType):
923     _validURIs = (NS.XSD3,)
924
925     def _checkValueSpace(self, data):
926         if data == None:
927             raise ValueError, "must supply initial %s value" % self._type
928
929         if type(data) not in (StringType, UnicodeType):
930             raise AttributeError, "invalid %s type" % self._type
931
932         return data
933
934     def _marshalData(self):
935         if self._cache == None:
936             self._cache = encodeHexString(self._data)
937
938         return self._cache
939
940 class base64BinaryType(anyType):
941     _validURIs = (NS.XSD3,)
942
943     def _checkValueSpace(self, data):
944         if data == None:
945             raise ValueError, "must supply initial %s value" % self._type
946
947         if type(data) not in (StringType, UnicodeType):
948             raise AttributeError, "invalid %s type" % self._type
949
950         return data
951
952     def _marshalData(self):
953         if self._cache == None:
954             self._cache = base64.encodestring(self._data)
955
956         return self._cache
957
958 class base64Type(base64BinaryType):
959     _validURIs = (NS.ENC,)
960
961 class binaryType(anyType):
962     _validURIs = (NS.XSD, NS.ENC)
963
964     def __init__(self, data, name = None, typed = 1, encoding = 'base64',
965         attrs = None):
966
967         anyType.__init__(self, data, name, typed, attrs)
968
969         self._setAttr('encoding', encoding)
970
971     def _marshalData(self):
972         if self._cache == None:
973             if self._getAttr((None, 'encoding')) == 'base64':
974                 self._cache = base64.encodestring(self._data)
975             else:
976                 self._cache = encodeHexString(self._data)
977
978         return self._cache
979
980     def _checkValueSpace(self, data):
981         if data == None:
982             raise ValueError, "must supply initial %s value" % self._type
983
984         if type(data) not in (StringType, UnicodeType):
985             raise AttributeError, "invalid %s type" % self._type
986
987         return data
988
989     def _setAttr(self, attr, value):
990         attr = self._fixAttr(attr)
991
992         if attr[1] == 'encoding':
993             if attr[0] != None or value not in ('base64', 'hex'):
994                 raise AttributeError, "invalid encoding"
995
996             self._cache = None
997
998         anyType._setAttr(self, attr, value)
999
1000
1001 class anyURIType(anyType):
1002     _validURIs = (NS.XSD3,)
1003
1004     def _checkValueSpace(self, data):
1005         if data == None:
1006             raise ValueError, "must supply initial %s value" % self._type
1007
1008         if type(data) not in (StringType, UnicodeType):
1009             raise AttributeError, "invalid %s type" % self._type
1010
1011         return data
1012
1013     def _marshalData(self):
1014         if self._cache == None:
1015             self._cache = urllib.quote(self._data)
1016
1017         return self._cache
1018
1019 class uriType(anyURIType):
1020     _validURIs = (NS.XSD,)
1021
1022 class uriReferenceType(anyURIType):
1023     _validURIs = (NS.XSD2,)
1024
1025 class NOTATIONType(anyType):
1026     def __init__(self, data, name = None, typed = 1, attrs = None):
1027
1028         if self.__class__ == NOTATIONType:
1029             raise Error, "a NOTATION can't be instantiated directly"
1030
1031         anyType.__init__(self, data, name, typed, attrs)
1032
1033 class ENTITIESType(anyType):
1034     def _checkValueSpace(self, data):
1035         if data == None:
1036             raise ValueError, "must supply initial %s value" % self._type
1037
1038         if type(data) in (StringType, UnicodeType):
1039             return (data,)
1040
1041         if type(data) not in (ListType, TupleType) or \
1042             filter (lambda x: type(x) not in (StringType, UnicodeType), data):
1043             raise AttributeError, "invalid %s type" % self._type
1044
1045         return data
1046
1047     def _marshalData(self):
1048         return ' '.join(self._data)
1049
1050 class IDREFSType(ENTITIESType): pass
1051 class NMTOKENSType(ENTITIESType): pass
1052
1053 class integerType(anyType):
1054     def _checkValueSpace(self, data):
1055         if data == None:
1056             raise ValueError, "must supply initial %s value" % self._type
1057
1058         if type(data) not in (IntType, LongType):
1059             raise ValueError, "invalid %s value" % self._type
1060
1061         return data
1062
1063 class nonPositiveIntegerType(anyType):
1064     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1065
1066     def _checkValueSpace(self, data):
1067         if data == None:
1068             raise ValueError, "must supply initial %s value" % self._type
1069
1070         if type(data) not in (IntType, LongType) or data > 0:
1071             raise ValueError, "invalid %s value" % self._type
1072
1073         return data
1074
1075 class non_Positive_IntegerType(nonPositiveIntegerType):
1076     _validURIs = (NS.XSD,)
1077
1078     def _typeName(self):
1079         return 'non-positive-integer'
1080
1081 class negativeIntegerType(anyType):
1082     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1083
1084     def _checkValueSpace(self, data):
1085         if data == None:
1086             raise ValueError, "must supply initial %s value" % self._type
1087
1088         if type(data) not in (IntType, LongType) or data >= 0:
1089             raise ValueError, "invalid %s value" % self._type
1090
1091         return data
1092
1093 class negative_IntegerType(negativeIntegerType):
1094     _validURIs = (NS.XSD,)
1095
1096     def _typeName(self):
1097         return 'negative-integer'
1098
1099 class longType(anyType):
1100     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1101
1102     def _checkValueSpace(self, data):
1103         if data == None:
1104             raise ValueError, "must supply initial %s value" % self._type
1105
1106         if type(data) not in (IntType, LongType) or \
1107             data < -9223372036854775808L or \
1108             data >  9223372036854775807L:
1109             raise ValueError, "invalid %s value" % self._type
1110
1111         return data
1112
1113 class intType(anyType):
1114     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1115
1116     def _checkValueSpace(self, data):
1117         if data == None:
1118             raise ValueError, "must supply initial %s value" % self._type
1119
1120         if type(data) not in (IntType, LongType) or \
1121             data < -2147483648L or \
1122             data >  2147483647L:
1123             raise ValueError, "invalid %s value" % self._type
1124
1125         return data
1126
1127 class shortType(anyType):
1128     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1129
1130     def _checkValueSpace(self, data):
1131         if data == None:
1132             raise ValueError, "must supply initial %s value" % self._type
1133
1134         if type(data) not in (IntType, LongType) or \
1135             data < -32768 or \
1136             data >  32767:
1137             raise ValueError, "invalid %s value" % self._type
1138
1139         return data
1140
1141 class byteType(anyType):
1142     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1143
1144     def _checkValueSpace(self, data):
1145         if data == None:
1146             raise ValueError, "must supply initial %s value" % self._type
1147
1148         if type(data) not in (IntType, LongType) or \
1149             data < -128 or \
1150             data >  127:
1151             raise ValueError, "invalid %s value" % self._type
1152
1153         return data
1154
1155 class nonNegativeIntegerType(anyType):
1156     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1157
1158     def _checkValueSpace(self, data):
1159         if data == None:
1160             raise ValueError, "must supply initial %s value" % self._type
1161
1162         if type(data) not in (IntType, LongType) or data < 0:
1163             raise ValueError, "invalid %s value" % self._type
1164
1165         return data
1166
1167 class non_Negative_IntegerType(nonNegativeIntegerType):
1168     _validURIs = (NS.XSD,)
1169
1170     def _typeName(self):
1171         return 'non-negative-integer'
1172
1173 class unsignedLongType(anyType):
1174     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1175
1176     def _checkValueSpace(self, data):
1177         if data == None:
1178             raise ValueError, "must supply initial %s value" % self._type
1179
1180         if type(data) not in (IntType, LongType) or \
1181             data < 0 or \
1182             data > 18446744073709551615L:
1183             raise ValueError, "invalid %s value" % self._type
1184
1185         return data
1186
1187 class unsignedIntType(anyType):
1188     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1189
1190     def _checkValueSpace(self, data):
1191         if data == None:
1192             raise ValueError, "must supply initial %s value" % self._type
1193
1194         if type(data) not in (IntType, LongType) or \
1195             data < 0 or \
1196             data > 4294967295L:
1197             raise ValueError, "invalid %s value" % self._type
1198
1199         return data
1200
1201 class unsignedShortType(anyType):
1202     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1203
1204     def _checkValueSpace(self, data):
1205         if data == None:
1206             raise ValueError, "must supply initial %s value" % self._type
1207
1208         if type(data) not in (IntType, LongType) or \
1209             data < 0 or \
1210             data > 65535:
1211             raise ValueError, "invalid %s value" % self._type
1212
1213         return data
1214
1215 class unsignedByteType(anyType):
1216     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1217
1218     def _checkValueSpace(self, data):
1219         if data == None:
1220             raise ValueError, "must supply initial %s value" % self._type
1221
1222         if type(data) not in (IntType, LongType) or \
1223             data < 0 or \
1224             data > 255:
1225             raise ValueError, "invalid %s value" % self._type
1226
1227         return data
1228
1229 class positiveIntegerType(anyType):
1230     _validURIs = (NS.XSD2, NS.XSD3, NS.ENC)
1231
1232     def _checkValueSpace(self, data):
1233         if data == None:
1234             raise ValueError, "must supply initial %s value" % self._type
1235
1236         if type(data) not in (IntType, LongType) or data <= 0:
1237             raise ValueError, "invalid %s value" % self._type
1238
1239         return data
1240
1241 class positive_IntegerType(positiveIntegerType):
1242     _validURIs = (NS.XSD,)
1243
1244     def _typeName(self):
1245         return 'positive-integer'
1246
1247 # Now compound types
1248
1249 class compoundType(anyType):
1250     def __init__(self, data = None, name = None, typed = 1, attrs = None):
1251         if self.__class__ == compoundType:
1252             raise Error, "a compound can't be instantiated directly"
1253
1254         anyType.__init__(self, data, name, typed, attrs)
1255         self._keyord    = []
1256
1257         if type(data) == DictType:
1258             self.__dict__.update(data)
1259
1260     def _aslist(self, item=None):
1261         if item is not None:
1262             return self.__dict__[self._keyord[item]]
1263         else:
1264             return map( lambda x: self.__dict__[x], self._keyord)
1265
1266     def _asdict(self, item=None, encoding=Config.dict_encoding):
1267         if item is not None:
1268             if type(item) in (UnicodeType,StringType):
1269                 item = item.encode(encoding)
1270             return self.__dict__[item]
1271         else:
1272             retval = {}
1273             def fun(x): retval[x.encode(encoding)] = self.__dict__[x]
1274
1275             if hasattr(self, '_keyord'):
1276                 map( fun, self._keyord)
1277             else:
1278                 for name in dir(self):
1279                     if isPublic(name):
1280                         retval[name] = getattr(self,name)
1281             return retval
1282
1283  
1284     def __getitem__(self, item):
1285         if type(item) == IntType:
1286             return self.__dict__[self._keyord[item]]
1287         else:
1288             return getattr(self, item)
1289
1290     def __len__(self):
1291         return len(self._keyord)
1292
1293     def __nonzero__(self):
1294         return 1
1295
1296     def _keys(self):
1297         return filter(lambda x: x[0] != '_', self.__dict__.keys())
1298
1299     def _addItem(self, name, value, attrs = None):
1300
1301         if name in self._keyord:
1302             if type(self.__dict__[name]) != ListType:
1303                 self.__dict__[name] = [self.__dict__[name]]
1304             self.__dict__[name].append(value)
1305         else:
1306             self.__dict__[name] = value
1307             self._keyord.append(name)
1308             
1309     def _placeItem(self, name, value, pos, subpos = 0, attrs = None):
1310
1311         if subpos == 0 and type(self.__dict__[name]) != ListType:
1312             self.__dict__[name] = value
1313         else:
1314             self.__dict__[name][subpos] = value
1315
1316         # only add to key order list if it does not already 
1317         # exist in list
1318         if not (name in self._keyord):
1319             if pos < len(x):
1320                 self._keyord[pos] = name
1321             else:
1322                 self._keyord.append(name)
1323               
1324
1325     def _getItemAsList(self, name, default = []):
1326         try:
1327             d = self.__dict__[name]
1328         except:
1329             return default
1330
1331         if type(d) == ListType:
1332             return d
1333         return [d]
1334
1335     def __str__(self):
1336         return anyType.__str__(self) + ": " + str(self._asdict())
1337
1338     def __repr__(self):
1339         return self.__str__()
1340
1341 class structType(compoundType):
1342     pass
1343
1344 class headerType(structType):
1345     _validURIs = (NS.ENV,)
1346
1347     def __init__(self, data = None, typed = 1, attrs = None):
1348         structType.__init__(self, data, "Header", typed, attrs)
1349
1350 class bodyType(structType):
1351     _validURIs = (NS.ENV,)
1352
1353     def __init__(self, data = None, typed = 1, attrs = None):
1354         structType.__init__(self, data, "Body", typed, attrs)
1355
1356 class arrayType(UserList.UserList, compoundType):
1357     def __init__(self, data = None, name = None, attrs = None,
1358         offset = 0, rank = None, asize = 0, elemsname = None):
1359
1360         if data:
1361             if type(data) not in (ListType, TupleType):
1362                 raise Error, "Data must be a sequence"
1363
1364         UserList.UserList.__init__(self, data)
1365         compoundType.__init__(self, data, name, 0, attrs)
1366
1367         self._elemsname = elemsname or "item"
1368
1369         if data == None:
1370             self._rank = rank
1371
1372             # According to 5.4.2.2 in the SOAP spec, each element in a
1373             # sparse array must have a position. _posstate keeps track of
1374             # whether we've seen a position or not. It's possible values
1375             # are:
1376             # -1 No elements have been added, so the state is indeterminate
1377             #  0 An element without a position has been added, so no
1378             #    elements can have positions
1379             #  1 An element with a position has been added, so all elements
1380             #    must have positions
1381
1382             self._posstate = -1
1383
1384             self._full = 0
1385
1386             if asize in ('', None):
1387                 asize = '0'
1388
1389             self._dims = map (lambda x: int(x), str(asize).split(','))
1390             self._dims.reverse()   # It's easier to work with this way
1391             self._poss = [0] * len(self._dims)      # This will end up
1392                                                     # reversed too
1393
1394             for i in range(len(self._dims)):
1395                 if self._dims[i] < 0 or \
1396                     self._dims[i] == 0 and len(self._dims) > 1:
1397                     raise TypeError, "invalid Array dimensions"
1398
1399                 if offset > 0:
1400                     self._poss[i] = offset % self._dims[i]
1401                     offset = int(offset / self._dims[i])
1402
1403                 # Don't break out of the loop if offset is 0 so we test all the
1404                 # dimensions for > 0.
1405             if offset:
1406                 raise AttributeError, "invalid Array offset"
1407
1408             a = [None] * self._dims[0]
1409
1410             for i in range(1, len(self._dims)):
1411                 b = []
1412
1413                 for j in range(self._dims[i]):
1414                     b.append(copy.deepcopy(a))
1415
1416                 a = b
1417
1418             self.data = a
1419
1420
1421     def _aslist(self, item=None):
1422         if item is not None:
1423             return self.data[int(item)]
1424         else:
1425             return self.data
1426
1427     def _asdict(self, item=None, encoding=Config.dict_encoding):
1428         if item is not None:
1429             if type(item) in (UnicodeType,StringType):
1430                 item = item.encode(encoding)
1431             return self.data[int(item)]
1432         else:
1433             retval = {}
1434             def fun(x): retval[str(x).encode(encoding)] = self.data[x]
1435             
1436             map( fun, range(len(self.data)) )
1437             return retval
1438  
1439     def __getitem__(self, item):
1440         try:
1441             return self.data[int(item)]
1442         except ValueError:
1443             return getattr(self, item)
1444
1445     def __len__(self):
1446         return len(self.data)
1447
1448     def __nonzero__(self):
1449         return 1
1450
1451     def __str__(self):
1452         return anyType.__str__(self) + ": " + str(self._aslist())
1453
1454     def _keys(self):
1455         return filter(lambda x: x[0] != '_', self.__dict__.keys())
1456
1457     def _addItem(self, name, value, attrs):
1458         if self._full:
1459             raise ValueError, "Array is full"
1460
1461         pos = attrs.get((NS.ENC, 'position'))
1462
1463         if pos != None:
1464             if self._posstate == 0:
1465                 raise AttributeError, \
1466                     "all elements in a sparse Array must have a " \
1467                     "position attribute"
1468
1469             self._posstate = 1
1470
1471             try:
1472                 if pos[0] == '[' and pos[-1] == ']':
1473                     pos = map (lambda x: int(x), pos[1:-1].split(','))
1474                     pos.reverse()
1475
1476                     if len(pos) == 1:
1477                         pos = pos[0]
1478
1479                         curpos = [0] * len(self._dims)
1480
1481                         for i in range(len(self._dims)):
1482                             curpos[i] = pos % self._dims[i]
1483                             pos = int(pos / self._dims[i])
1484
1485                             if pos == 0:
1486                                 break
1487
1488                         if pos:
1489                             raise Exception
1490                     elif len(pos) != len(self._dims):
1491                         raise Exception
1492                     else:
1493                         for i in range(len(self._dims)):
1494                             if pos[i] >= self._dims[i]:
1495                                 raise Exception
1496
1497                         curpos = pos
1498                 else:
1499                     raise Exception
1500             except:
1501                 raise AttributeError, \
1502                     "invalid Array element position %s" % str(pos)
1503         else:
1504             if self._posstate == 1:
1505                 raise AttributeError, \
1506                     "only elements in a sparse Array may have a " \
1507                     "position attribute"
1508
1509             self._posstate = 0
1510
1511             curpos = self._poss
1512
1513         a = self.data
1514
1515         for i in range(len(self._dims) - 1, 0, -1):
1516             a = a[curpos[i]]
1517
1518         if curpos[0] >= len(a):
1519             a += [None] * (len(a) - curpos[0] + 1)
1520
1521         a[curpos[0]] = value
1522
1523         if pos == None:
1524             self._poss[0] += 1
1525
1526             for i in range(len(self._dims) - 1):
1527                 if self._poss[i] < self._dims[i]:
1528                     break
1529
1530                 self._poss[i] = 0
1531                 self._poss[i + 1] += 1
1532
1533             if self._dims[-1] and self._poss[-1] >= self._dims[-1]:
1534                 #self._full = 1
1535                 #FIXME: why is this occuring?
1536                 pass
1537
1538     def _placeItem(self, name, value, pos, subpos, attrs = None):
1539         curpos = [0] * len(self._dims)
1540
1541         for i in range(len(self._dims)):
1542             if self._dims[i] == 0:
1543                 curpos[0] = pos
1544                 break
1545
1546             curpos[i] = pos % self._dims[i]
1547             pos = int(pos / self._dims[i])
1548
1549             if pos == 0:
1550                 break
1551
1552         if self._dims[i] != 0 and pos:
1553             raise Error, "array index out of range"
1554
1555         a = self.data
1556
1557         for i in range(len(self._dims) - 1, 0, -1):
1558             a = a[curpos[i]]
1559
1560         if curpos[0] >= len(a):
1561             a += [None] * (len(a) - curpos[0] + 1)
1562
1563         a[curpos[0]] = value
1564
1565 class typedArrayType(arrayType):
1566     def __init__(self, data = None, name = None, typed = None, attrs = None,
1567         offset = 0, rank = None, asize = 0, elemsname = None, complexType = 0):
1568
1569         arrayType.__init__(self, data, name, attrs, offset, rank, asize,
1570             elemsname)
1571
1572         self._typed = 1
1573         self._type = typed
1574         self._complexType = complexType
1575
1576 class faultType(structType, Error):
1577     def __init__(self, faultcode = "", faultstring = "", detail = None):
1578         self.faultcode = faultcode
1579         self.faultstring = faultstring
1580         if detail != None:
1581             self.detail = detail
1582
1583         structType.__init__(self, None, 0)
1584
1585     def _setDetail(self, detail = None):
1586         if detail != None:
1587             self.detail = detail
1588         else:
1589             try: del self.detail
1590             except AttributeError: pass
1591
1592     def __repr__(self):
1593         if getattr(self, 'detail', None) != None:
1594             return "<Fault %s: %s: %s>" % (self.faultcode,
1595                                            self.faultstring,
1596                                            self.detail)
1597         else:
1598             return "<Fault %s: %s>" % (self.faultcode, self.faultstring)
1599
1600     __str__ = __repr__
1601
1602     def __call__(self):
1603         return (self.faultcode, self.faultstring, self.detail)        
1604
1605 class SOAPException(Exception):
1606     def __init__(self, code="", string="", detail=None):
1607         self.value = ("SOAPpy SOAP Exception", code, string, detail)
1608         self.code = code
1609         self.string = string
1610         self.detail = detail
1611
1612     def __str__(self):
1613         return repr(self.value)
1614
1615 class RequiredHeaderMismatch(Exception):
1616     def __init__(self, value):
1617         self.value = value
1618
1619     def __str__(self):
1620         return repr(self.value)
1621
1622 class MethodNotFound(Exception):
1623     def __init__(self, value):
1624         (val, detail) = value.split(":")
1625         self.value = val
1626         self.detail = detail
1627
1628     def __str__(self):
1629         return repr(self.value, self.detail)
1630
1631 class AuthorizationFailed(Exception):
1632     def __init__(self, value):
1633         self.value = value
1634
1635     def __str__(self):
1636         return repr(self.value)
1637
1638 class MethodFailed(Exception):
1639     def __init__(self, value):
1640         self.value = value
1641
1642     def __str__(self):
1643         return repr(self.value)
1644         
1645 #######
1646 # Convert complex SOAPpy objects to native python equivalents
1647 #######
1648
1649 def simplify(object, level=0):
1650     """
1651     Convert the SOAPpy objects and their contents to simple python types.
1652
1653     This function recursively converts the passed 'container' object,
1654     and all public subobjects. (Private subobjects have names that
1655     start with '_'.)
1656     
1657     Conversions:
1658     - faultType    --> raise python exception
1659     - arrayType    --> array
1660     - compoundType --> dictionary
1661     """
1662     
1663     if level > 10:
1664         return object
1665     
1666     if isinstance( object, faultType ):
1667         if object.faultstring == "Required Header Misunderstood":
1668             raise RequiredHeaderMismatch(object.detail)
1669         elif object.faultstring == "Method Not Found":
1670             raise MethodNotFound(object.detail)
1671         elif object.faultstring == "Authorization Failed":
1672             raise AuthorizationFailed(object.detail)
1673         elif object.faultstring == "Method Failed":
1674             raise MethodFailed(object.detail)
1675         else:
1676             se = SOAPException(object.faultcode, object.faultstring,
1677                                object.detail)
1678             raise se
1679     elif isinstance( object, arrayType ):
1680         data = object._aslist()
1681         for k in range(len(data)):
1682             data[k] = simplify(data[k], level=level+1)
1683         return data
1684     elif isinstance( object, compoundType ) or isinstance(object, structType):
1685         data = object._asdict()
1686         for k in data.keys():
1687             if isPublic(k):
1688                 data[k] = simplify(data[k], level=level+1)
1689         return data
1690     elif type(object)==DictType:
1691         for k in object.keys():
1692             if isPublic(k):
1693                 object[k] = simplify(object[k])
1694         return object
1695     elif type(object)==list:
1696         for k in range(len(object)):
1697             object[k] = simplify(object[k])
1698         return object
1699     else:
1700         return object
1701
1702
1703 def simplify_contents(object, level=0):
1704     """
1705     Convert the contents of SOAPpy objects to simple python types.
1706
1707     This function recursively converts the sub-objects contained in a
1708     'container' object to simple python types.
1709     
1710     Conversions:
1711     - faultType    --> raise python exception
1712     - arrayType    --> array
1713     - compoundType --> dictionary
1714     """
1715     
1716     if level>10: return object
1717
1718     if isinstance( object, faultType ):
1719         for k in object._keys():
1720             if isPublic(k):
1721                 setattr(object, k, simplify(object[k], level=level+1))
1722         raise object
1723     elif isinstance( object, arrayType ): 
1724         data = object._aslist()
1725         for k in range(len(data)):
1726             object[k] = simplify(data[k], level=level+1)
1727     elif isinstance(object, structType):
1728         data = object._asdict()
1729         for k in data.keys():
1730             if isPublic(k):
1731                 setattr(object, k, simplify(data[k], level=level+1))
1732     elif isinstance( object, compoundType ) :
1733         data = object._asdict()
1734         for k in data.keys():
1735             if isPublic(k):
1736                 object[k] = simplify(data[k], level=level+1)
1737     elif type(object)==DictType:
1738         for k in object.keys():
1739             if isPublic(k):
1740                 object[k] = simplify(object[k])
1741     elif type(object)==list:
1742         for k in range(len(object)):
1743             object[k] = simplify(object[k])
1744     
1745     return object
1746
1747