00001
00002
00003 '''XML Schema support
00004 '''
00005
00006 from ZSI import _copyright, _seqtypes, _find_type, _get_element_nsuri_name, EvaluateException
00007 from ZSI.wstools.Namespaces import SCHEMA, SOAP
00008 from ZSI.wstools.Utility import SplitQName
00009
00010
00011 def _get_type_definition(namespaceURI, name, **kw):
00012 return SchemaInstanceType.getTypeDefinition(namespaceURI, name, **kw)
00013
00014 def _get_global_element_declaration(namespaceURI, name, **kw):
00015 return SchemaInstanceType.getElementDeclaration(namespaceURI, name, **kw)
00016
00017 def _get_substitute_element(head, elt, ps):
00018 '''if elt matches a member of the head substitutionGroup, return
00019 the GED typecode.
00020
00021 head -- ElementDeclaration typecode,
00022 elt -- the DOM element being parsed
00023 ps -- ParsedSoap Instance
00024 '''
00025 if not isinstance(head, ElementDeclaration):
00026 return None
00027
00028 return ElementDeclaration.getSubstitutionElement(head, elt, ps)
00029
00030 def _has_type_definition(namespaceURI, name):
00031 return SchemaInstanceType.getTypeDefinition(namespaceURI, name) is not None
00032
00033 def _is_substitute_element(head, sub):
00034 '''if head and sub are both GEDs, and sub declares
00035 head as its substitutionGroup then return True.
00036
00037 head -- Typecode instance
00038 sub -- Typecode instance
00039 '''
00040 if not isinstance(head, ElementDeclaration) or not isinstance(sub, ElementDeclaration):
00041 return False
00042
00043 try:
00044 group = sub.substitutionGroup
00045 except (AttributeError, TypeError):
00046 return False
00047
00048 ged = GED(*group)
00049
00050
00051
00052 print (head.nspname == ged.nspname and head.pname == ged.pname)
00053 if head is ged or not (head.nspname == ged.nspname and head.pname == ged.pname):
00054 return False
00055
00056 return True
00057
00058
00059
00060
00061
00062 GED = _get_global_element_declaration
00063 GTD = _get_type_definition
00064
00065
00066 def WrapImmutable(pyobj, what):
00067 '''Wrap immutable instance so a typecode can be
00068 set, making it self-describing ie. serializable.
00069 '''
00070 return _GetPyobjWrapper.WrapImmutable(pyobj, what)
00071
00072 def RegisterBuiltin(arg):
00073 '''Add a builtin to be registered, and register it
00074 with the Any typecode.
00075 '''
00076 _GetPyobjWrapper.RegisterBuiltin(arg)
00077 _GetPyobjWrapper.RegisterAnyElement()
00078
00079 def RegisterAnyElement():
00080 '''register all Wrapper classes with the Any typecode.
00081 This allows instances returned by Any to be self-describing.
00082 ie. serializable. AnyElement falls back on Any to parse
00083 anything it doesn't understand.
00084 '''
00085 return _GetPyobjWrapper.RegisterAnyElement()
00086
00087
00088 class SchemaInstanceType(type):
00089 '''Register all types/elements, when hit already defined
00090 class dont create a new one just give back reference. Thus
00091 import order determines which class is loaded.
00092
00093 class variables:
00094 types -- dict of typecode classes definitions
00095 representing global type definitions.
00096 elements -- dict of typecode classes representing
00097 global element declarations.
00098 element_typecode_cache -- dict of typecode instances
00099 representing global element declarations.
00100 '''
00101 types = {}
00102 elements = {}
00103 element_typecode_cache = {}
00104
00105
00106 def __new__(cls,classname,bases,classdict):
00107 '''If classdict has literal and schema register it as a
00108 element declaration, else if has type and schema register
00109 it as a type definition.
00110 '''
00111 if classname in ['ElementDeclaration', 'TypeDefinition', 'LocalElementDeclaration',]:
00112 return type.__new__(cls,classname,bases,classdict)
00113
00114 if ElementDeclaration in bases:
00115 if classdict.has_key('schema') is False or classdict.has_key('literal') is False:
00116 raise AttributeError, 'ElementDeclaration must define schema and literal attributes'
00117
00118 key = (classdict['schema'],classdict['literal'])
00119 if SchemaInstanceType.elements.has_key(key):
00120 return SchemaInstanceType.elements[key]
00121
00122
00123 ged = SchemaInstanceType.elements[key] = type.__new__(cls,classname,bases,classdict)
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134 return ged
00135
00136 if TypeDefinition in bases:
00137 if classdict.has_key('type') is None:
00138 raise AttributeError, 'TypeDefinition must define type attribute'
00139
00140 key = classdict['type']
00141 if SchemaInstanceType.types.has_key(key) is False:
00142 SchemaInstanceType.types[key] = type.__new__(cls,classname,bases,classdict)
00143 return SchemaInstanceType.types[key]
00144
00145 if LocalElementDeclaration in bases:
00146 return type.__new__(cls,classname,bases,classdict)
00147
00148 raise TypeError, 'SchemaInstanceType must be an ElementDeclaration or TypeDefinition '
00149
00150 def getTypeDefinition(cls, namespaceURI, name, lazy=False):
00151 '''Grab a type definition, returns a typecode class definition
00152 because the facets (name, minOccurs, maxOccurs) must be provided.
00153
00154 Parameters:
00155 namespaceURI --
00156 name --
00157 '''
00158 klass = cls.types.get((namespaceURI, name), None)
00159 if lazy and klass is not None:
00160 return _Mirage(klass)
00161 return klass
00162 getTypeDefinition = classmethod(getTypeDefinition)
00163
00164 def getElementDeclaration(cls, namespaceURI, name, isref=False, lazy=False):
00165 '''Grab an element declaration, returns a typecode instance
00166 representation or a typecode class definition. An element
00167 reference has its own facets, and is local so it will not be
00168 cached.
00169
00170 Parameters:
00171 namespaceURI --
00172 name --
00173 isref -- if element reference, return class definition.
00174 '''
00175 key = (namespaceURI, name)
00176 if isref:
00177 klass = cls.elements.get(key,None)
00178 if klass is not None and lazy is True:
00179 return _Mirage(klass)
00180 return klass
00181
00182 typecode = cls.element_typecode_cache.get(key, None)
00183 if typecode is None:
00184 tcls = cls.elements.get(key,None)
00185 if tcls is not None:
00186 typecode = cls.element_typecode_cache[key] = tcls()
00187 typecode.typed = False
00188
00189 return typecode
00190 getElementDeclaration = classmethod(getElementDeclaration)
00191
00192
00193 class ElementDeclaration:
00194 '''Typecodes subclass to represent a Global Element Declaration by
00195 setting class variables schema and literal.
00196
00197 schema = namespaceURI
00198 literal = NCName
00199 substitutionGroup -- GED reference of form, (namespaceURI,NCName)
00200 '''
00201 __metaclass__ = SchemaInstanceType
00202
00203 def checkSubstitute(self, typecode):
00204 '''If this is True, allow typecode to be substituted
00205 for "self" typecode.
00206 '''
00207 if not isinstance(typecode, ElementDeclaration):
00208 return False
00209
00210 try:
00211 nsuri,ncname = typecode.substitutionGroup
00212 except (AttributeError, TypeError):
00213 return False
00214
00215 if (nsuri,ncname) != (self.schema,self.literal):
00216
00217 if not nsuri and not self.schema and ncname == self.literal:
00218 return True
00219
00220 return False
00221
00222 sub = GED(self.schema, self.literal)
00223 if sub is None or sub is not typecode:
00224 return False
00225
00226 return True
00227
00228 def getSubstitutionElement(self, elt, ps):
00229 '''if elt matches a member of the head substitutionGroup, return
00230 the GED typecode representation of the member.
00231
00232 head -- ElementDeclaration typecode,
00233 elt -- the DOM element being parsed
00234 ps -- ParsedSoap instance
00235 '''
00236 nsuri,ncname = _get_element_nsuri_name(elt)
00237 typecode = GED(nsuri,ncname)
00238 if typecode is None:
00239 return
00240
00241 try:
00242 nsuri,ncname = typecode.substitutionGroup
00243 except (AttributeError, TypeError):
00244 return
00245
00246 if (ncname == self.pname) and (nsuri == self.nspname or
00247 (not nsuri and not self.nspname)):
00248 return typecode
00249
00250 return
00251
00252
00253 class LocalElementDeclaration:
00254 '''Typecodes subclass to represent a Local Element Declaration.
00255 '''
00256 __metaclass__ = SchemaInstanceType
00257
00258
00259 class TypeDefinition:
00260 '''Typecodes subclass to represent a Global Type Definition by
00261 setting class variable type.
00262
00263 type = (namespaceURI, NCName)
00264 '''
00265 __metaclass__ = SchemaInstanceType
00266
00267 def getSubstituteType(self, elt, ps):
00268 '''if xsi:type does not match the instance type attr,
00269 check to see if it is a derived type substitution.
00270
00271 DONT Return the element's type.
00272
00273 Parameters:
00274 elt -- the DOM element being parsed
00275 ps -- the ParsedSoap object.
00276 '''
00277 pyclass = SchemaInstanceType.getTypeDefinition(*self.type)
00278 if pyclass is None:
00279 raise EvaluateException(
00280 'No Type registed for xsi:type=(%s, %s)' %
00281 (self.type[0], self.type[1]), ps.Backtrace(elt))
00282
00283 typeName = _find_type(elt)
00284 prefix,typeName = SplitQName(typeName)
00285 uri = ps.GetElementNSdict(elt).get(prefix)
00286 subclass = SchemaInstanceType.getTypeDefinition(uri, typeName)
00287 if subclass is None:
00288 raise EvaluateException(
00289 'No registered xsi:type=(%s, %s), substitute for xsi:type=(%s, %s)' %
00290 (uri, typeName, self.type[0], self.type[1]), ps.Backtrace(elt))
00291
00292 if not issubclass(subclass, pyclass) and subclass(None) and not issubclass(subclass, pyclass):
00293 raise TypeError(
00294 'Substitute Type (%s, %s) is not derived from %s' %
00295 (self.type[0], self.type[1], pyclass), ps.Backtrace(elt))
00296
00297 return subclass((self.nspname, self.pname))
00298
00299
00300
00301 class _Mirage:
00302 '''Used with SchemaInstanceType for lazy evaluation, eval during serialize or
00303 parse as needed. Mirage is callable, TypeCodes are not. When called it returns the
00304 typecode. Tightly coupled with generated code.
00305
00306 NOTE: **Must Use ClassType** for intended MRO of __call__ since setting it in
00307 an instance attribute rather than a class attribute (will not work for object).
00308 '''
00309 def __init__(self, klass):
00310 self.klass = klass
00311 self.__reveal = False
00312 self.__cache = None
00313 if issubclass(klass, ElementDeclaration):
00314 self.__call__ = self._hide_element
00315
00316 def __str__(self):
00317 msg = "<Mirage id=%s, Local Element %s>"
00318 if issubclass(self.klass, ElementDeclaration):
00319 msg = "<Mirage id=%s, GED %s>"
00320 return msg %(id(self), self.klass)
00321
00322 def _hide_type(self, pname, aname, minOccurs=0, maxOccurs=1, nillable=False,
00323 **kw):
00324 self.__call__ = self._reveal_type
00325 self.__reveal = True
00326
00327
00328 self.__kw = kw
00329 self.minOccurs,self.maxOccurs,self.nillable = minOccurs,maxOccurs,nillable
00330 self.nspname,self.pname,self.aname = None,pname,aname
00331 if type(self.pname) in (tuple,list):
00332 self.nspname,self.pname = pname
00333
00334 return self
00335
00336 def _hide_element(self, minOccurs=0, maxOccurs=1, nillable=False, **kw):
00337 self.__call__ = self._reveal_element
00338 self.__reveal = True
00339
00340
00341 self.__kw = kw
00342 self.nspname = self.klass.schema
00343 self.pname = self.klass.literal
00344
00345
00346 self.minOccurs,self.maxOccurs,self.nillable = minOccurs,maxOccurs,nillable
00347
00348 return self
00349
00350 def _reveal_type(self):
00351 if self.__cache is None:
00352 self.__cache = self.klass(pname=self.pname,
00353 aname=self.aname, minOccurs=self.minOccurs,
00354 maxOccurs=self.maxOccurs, nillable=self.nillable,
00355 **self.__kw)
00356 return self.__cache
00357
00358 def _reveal_element(self):
00359 if self.__cache is None:
00360 self.__cache = self.klass(minOccurs=self.minOccurs,
00361 maxOccurs=self.maxOccurs, nillable=self.nillable,
00362 **self.__kw)
00363 return self.__cache
00364
00365 __call__ = _hide_type
00366
00367
00368 class _GetPyobjWrapper:
00369 '''Get a python object that wraps data and typecode. Used by
00370 <any> parse routine, so that typecode information discovered
00371 during parsing is retained in the pyobj representation
00372 and thus can be serialized.
00373 '''
00374 types_dict = dict()
00375
00376 def RegisterBuiltin(cls, arg):
00377 '''register a builtin, create a new wrapper.
00378 '''
00379 if arg in cls.types_dict:
00380 raise RuntimeError, '%s already registered' %arg
00381 class _Wrapper(arg):
00382 'Wrapper for builtin %s\n%s' %(arg, cls.__doc__)
00383 _Wrapper.__name__ = '_%sWrapper' %arg.__name__
00384 cls.types_dict[arg] = _Wrapper
00385 RegisterBuiltin = classmethod(RegisterBuiltin)
00386
00387 def RegisterAnyElement(cls):
00388 '''If find registered TypeCode instance, add Wrapper class
00389 to TypeCode class serialmap and Re-RegisterType. Provides
00390 Any serialzation of any instances of the Wrapper.
00391 '''
00392 for k,v in cls.types_dict.items():
00393 what = Any.serialmap.get(k)
00394 if what is None: continue
00395 if v in what.__class__.seriallist: continue
00396 what.__class__.seriallist.append(v)
00397 RegisterType(what.__class__, clobber=1, **what.__dict__)
00398 RegisterAnyElement = classmethod(RegisterAnyElement)
00399
00400 def WrapImmutable(cls, pyobj, what):
00401 '''return a wrapper for pyobj, with typecode attribute set.
00402 Parameters:
00403 pyobj -- instance of builtin type (immutable)
00404 what -- typecode describing the data
00405 '''
00406 d = cls.types_dict
00407 if type(pyobj) is bool:
00408 pyclass = d[int]
00409 elif d.has_key(type(pyobj)) is True:
00410 pyclass = d[type(pyobj)]
00411 else:
00412 raise TypeError,\
00413 'Expecting a built-in type in %s (got %s).' %(
00414 d.keys(),type(pyobj))
00415
00416 newobj = pyclass(pyobj)
00417 newobj.typecode = what
00418 return newobj
00419 WrapImmutable = classmethod(WrapImmutable)
00420
00421
00422 from TC import Any, RegisterType
00423
00424 if __name__ == '__main__': print _copyright
00425