1############################################################################ 2# Joshua R. Boverhof, LBNL 3# See LBNLCopyright for copyright notice! 4########################################################################### 5 6import pydoc, sys, warnings 7from ZSI import TC 8 9# If function.__name__ is read-only, fail 10def _x(): return 11try: 12 _x.func_name = '_y' 13except: 14 raise RuntimeError,\ 15 'use python-2.4 or later, cannot set function names in python "%s"'\ 16 %sys.version 17del _x 18 19 20#def GetNil(typecode=None): 21# """returns the nilled element, use to set an element 22# as nilled for immutable instance. 23# """ 24# 25# nil = TC.Nilled() 26# if typecode is not None: nil.typecode = typecode 27# return nil 28# 29# 30#def GetNilAsSelf(cls, typecode=None): 31# """returns the nilled element with typecode specified, 32# use returned instance to set this element as nilled. 33# 34# Key Word Parameters: 35# typecode -- use to specify a derived type or wildcard as nilled. 36# """ 37# if typecode is not None and not isinstance(typecode, TC.TypeCode): 38# raise TypeError, "Expecting a TypeCode instance" 39# 40# nil = TC.Nilled() 41# nil.typecode = typecode or cls.typecode 42# return nil 43 44 45class pyclass_type(type): 46 """Stability: Unstable 47 48 type for pyclasses used with typecodes. expects the typecode to 49 be available in the classdict. creates python properties for accessing 50 and setting the elements specified in the ofwhat list, and factory methods 51 for constructing the elements. 52 53 Known Limitations: 54 1)Uses XML Schema element names directly to create method names, 55 using characters in this set will cause Syntax Errors: 56 57 (NCNAME)-(letter U digit U "_") 58 59 """ 60 def __new__(cls, classname, bases, classdict): 61 """ 62 """ 63 #import new 64 typecode = classdict.get('typecode') 65 assert typecode is not None, 'MUST HAVE A TYPECODE.' 66 67 # Assume this means immutable type. ie. str 68 if len(bases) > 0: 69 #classdict['new_Nill'] = classmethod(GetNilAsSelf) 70 pass 71 # Assume this means mutable type. ie. ofwhat. 72 else: 73 assert hasattr(typecode, 'ofwhat'), 'typecode has no ofwhat list??' 74 assert hasattr(typecode, 'attribute_typecode_dict'),\ 75 'typecode has no attribute_typecode_dict??' 76 77 #classdict['new_Nill'] = staticmethod(GetNil) 78 79 if typecode.mixed: 80 get,set = cls.__create_text_functions_from_what(typecode) 81 82 if classdict.has_key(get.__name__): 83 raise AttributeError,\ 84 'attribute %s previously defined.' %get.__name__ 85 86 if classdict.has_key(set.__name__): 87 raise AttributeError,\ 88 'attribute %s previously defined.' %set.__name__ 89 90 classdict[get.__name__] = get 91 classdict[set.__name__] = set 92 93 for what in typecode.ofwhat: 94 get,set,new_func = cls.__create_functions_from_what(what) 95 96 if classdict.has_key(get.__name__): 97 raise AttributeError,\ 98 'attribute %s previously defined.' %get.__name__ 99 100 classdict[get.__name__] = get 101 if classdict.has_key(set.__name__): 102 raise AttributeError,\ 103 'attribute %s previously defined.' %set.__name__ 104 105 classdict[set.__name__] = set 106 if new_func is not None: 107 if classdict.has_key(new_func.__name__): 108 raise AttributeError,\ 109 'attribute %s previously defined.' %new_func.__name__ 110 111 classdict[new_func.__name__] = new_func 112 113 assert not classdict.has_key(what.pname),\ 114 'collision with pname="%s", bail..' %what.pname 115 116 pname = what.pname 117 if pname is None and isinstance(what, TC.AnyElement): pname = 'any' 118 assert pname is not None, 'Element with no name: %s' %what 119 120 # TODO: for pname if keyword just uppercase first letter. 121 #if pydoc.Helper.keywords.has_key(pname): 122 pname = pname[0].upper() + pname[1:] 123 assert not pydoc.Helper.keywords.has_key(pname), 'unexpected keyword: %s' %pname 124 125 classdict[pname] =\ 126 property(get, set, None, 127 'property for element (%s,%s), minOccurs="%s" maxOccurs="%s" nillable="%s"'\ 128 %(what.nspname,what.pname,what.minOccurs,what.maxOccurs,what.nillable) 129 ) 130 131 # 132 # mutable type <complexType> complexContent | modelGroup 133 # or immutable type <complexType> simpleContent (float, str, etc) 134 # 135 if hasattr(typecode, 'attribute_typecode_dict'): 136 attribute_typecode_dict = typecode.attribute_typecode_dict or {} 137 for key,what in attribute_typecode_dict.items(): 138 get,set = cls.__create_attr_functions_from_what(key, what) 139 if classdict.has_key(get.__name__): 140 raise AttributeError,\ 141 'attribute %s previously defined.' %get.__name__ 142 143 if classdict.has_key(set.__name__): 144 raise AttributeError,\ 145 'attribute %s previously defined.' %set.__name__ 146 147 classdict[get.__name__] = get 148 classdict[set.__name__] = set 149 150 return type.__new__(cls,classname,bases,classdict) 151 152 def __create_functions_from_what(what): 153 if not callable(what): 154 def get(self): 155 return getattr(self, what.aname) 156 157 if what.maxOccurs > 1: 158 def set(self, value): 159 if not (value is None or hasattr(value, '__iter__')): 160 raise TypeError, 'expecting an iterable instance' 161 setattr(self, what.aname, value) 162 else: 163 def set(self, value): 164 setattr(self, what.aname, value) 165 else: 166 def get(self): 167 return getattr(self, what().aname) 168 169 if what.maxOccurs > 1: 170 def set(self, value): 171 if not (value is None or hasattr(value, '__iter__')): 172 raise TypeError, 'expecting an iterable instance' 173 setattr(self, what().aname, value) 174 else: 175 def set(self, value): 176 setattr(self, what().aname, value) 177 178 # 179 # new factory function 180 # if pyclass is None, skip 181 # 182 if not callable(what) and getattr(what, 'pyclass', None) is None: 183 new_func = None 184 elif (isinstance(what, TC.ComplexType) or 185 isinstance(what, TC.Array)): 186 187 def new_func(self): 188 '''returns a mutable type 189 ''' 190 return what.pyclass() 191 192 elif not callable(what): 193 194 def new_func(self, value): 195 '''value -- initialize value 196 returns an immutable type 197 ''' 198 return what.pyclass(value) 199 200 elif (issubclass(what.klass, TC.ComplexType) or 201 issubclass(what.klass, TC.Array)): 202 203 def new_func(self): 204 '''returns a mutable type or None (if no pyclass). 205 ''' 206 p = what().pyclass 207 if p is None: return 208 return p() 209 210 else: 211 212 def new_func(self, value=None): 213 '''if simpleType provide initialization value, else 214 if complexType value should be left as None. 215 Parameters: 216 value -- initialize value or None 217 218 returns a mutable instance (value is None) 219 or an immutable instance or None (if no pyclass) 220 ''' 221 p = what().pyclass 222 if p is None: return 223 if value is None: return p() 224 return p(value) 225 226 #TODO: sub all illegal characters in set 227 # (NCNAME)-(letter U digit U "_") 228 if new_func is not None: 229 new_func.__name__ = 'new_%s' %what.pname 230 get.func_name = 'get_element_%s' %what.pname 231 set.func_name = 'set_element_%s' %what.pname 232 return get,set,new_func 233 __create_functions_from_what = staticmethod(__create_functions_from_what) 234 235 def __create_attr_functions_from_what(key, what): 236 237 def get(self): 238 '''returns attribute value for attribute %s, else None. 239 ''' %str(key) 240 return getattr(self, what.attrs_aname, {}).get(key, None) 241 242 def set(self, value): 243 '''set value for attribute %s. 244 value -- initialize value, immutable type 245 ''' %str(key) 246 if not hasattr(self, what.attrs_aname): 247 setattr(self, what.attrs_aname, {}) 248 getattr(self, what.attrs_aname)[key] = value 249 250 #TODO: sub all illegal characters in set 251 # (NCNAME)-(letter U digit U "_") 252 if type(key) in (tuple, list): 253 get.__name__ = 'get_attribute_%s' %key[1] 254 set.__name__ = 'set_attribute_%s' %key[1] 255 else: 256 get.__name__ = 'get_attribute_%s' %key 257 set.__name__ = 'set_attribute_%s' %key 258 259 return get,set 260 __create_attr_functions_from_what = \ 261 staticmethod(__create_attr_functions_from_what) 262 263 def __create_text_functions_from_what(what): 264 265 def get(self): 266 '''returns text content, else None. 267 ''' 268 return getattr(self, what.mixed_aname, None) 269 270 get.im_func = 'get_text' 271 272 def set(self, value): 273 '''set text content. 274 value -- initialize value, immutable type 275 ''' 276 setattr(self, what.mixed_aname, value) 277 278 get.im_func = 'set_text' 279 280 return get,set 281 __create_text_functions_from_what = \ 282 staticmethod(__create_text_functions_from_what) 283 284 285 286