1# This program is free software; you can redistribute it and/or modify it under 2# the terms of the (LGPL) GNU Lesser General Public License as published by the 3# Free Software Foundation; either version 3 of the License, or (at your 4# option) any later version. 5# 6# This program is distributed in the hope that it will be useful, but WITHOUT 7# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 8# FOR A PARTICULAR PURPOSE. See the GNU Library Lesser General Public License 9# for more details at ( http://www.gnu.org/licenses/lgpl.html ). 10# 11# You should have received a copy of the GNU Lesser General Public License 12# along with this program; if not, write to the Free Software Foundation, Inc., 13# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 14# written by: Jeff Ortel ( jortel@redhat.com ) 15 16""" 17Provides a collection of suds objects primarily used for highly dynamic 18interactions with WSDL/XSD defined types. 19 20""" 21 22from suds import * 23 24from logging import getLogger 25log = getLogger(__name__) 26 27 28def items(sobject): 29 """ 30 Extract the I{items} from a suds object. 31 32 Much like the items() method works on I{dict}. 33 34 @param sobject: A suds object 35 @type sobject: L{Object} 36 @return: A list of items contained in I{sobject}. 37 @rtype: [(key, value),...] 38 39 """ 40 for item in sobject: 41 yield item 42 43 44def asdict(sobject): 45 """ 46 Convert a sudsobject into a dictionary. 47 48 @param sobject: A suds object 49 @type sobject: L{Object} 50 @return: A python dictionary containing the items contained in I{sobject}. 51 @rtype: dict 52 53 """ 54 return dict(items(sobject)) 55 56def merge(a, b): 57 """ 58 Merge all attributes and metadata from I{a} to I{b}. 59 60 @param a: A I{source} object 61 @type a: L{Object} 62 @param b: A I{destination} object 63 @type b: L{Object} 64 65 """ 66 for item in a: 67 setattr(b, item[0], item[1]) 68 b.__metadata__ = b.__metadata__ 69 return b 70 71def footprint(sobject): 72 """ 73 Get the I{virtual footprint} of the object. 74 75 This is really a count of all the significant value attributes in the 76 branch. 77 78 @param sobject: A suds object. 79 @type sobject: L{Object} 80 @return: The branch footprint. 81 @rtype: int 82 83 """ 84 n = 0 85 for a in sobject.__keylist__: 86 v = getattr(sobject, a) 87 if v is None: 88 continue 89 if isinstance(v, Object): 90 n += footprint(v) 91 continue 92 if hasattr(v, "__len__"): 93 if len(v): 94 n += 1 95 continue 96 n += 1 97 return n 98 99 100class Factory: 101 102 cache = {} 103 104 @classmethod 105 def subclass(cls, name, bases, dict={}): 106 if not isinstance(bases, tuple): 107 bases = (bases,) 108 # name is of type unicode in python 2 -> not accepted by type() 109 name = str(name) 110 key = ".".join((name, str(bases))) 111 subclass = cls.cache.get(key) 112 if subclass is None: 113 subclass = type(name, bases, dict) 114 cls.cache[key] = subclass 115 return subclass 116 117 @classmethod 118 def object(cls, classname=None, dict={}): 119 if classname is not None: 120 subclass = cls.subclass(classname, Object) 121 inst = subclass() 122 else: 123 inst = Object() 124 for a in list(dict.items()): 125 setattr(inst, a[0], a[1]) 126 return inst 127 128 @classmethod 129 def metadata(cls): 130 return Metadata() 131 132 @classmethod 133 def property(cls, name, value=None): 134 subclass = cls.subclass(name, Property) 135 return subclass(value) 136 137 138class Object(UnicodeMixin): 139 140 def __init__(self): 141 self.__keylist__ = [] 142 self.__printer__ = Printer() 143 self.__metadata__ = Metadata() 144 145 def __setattr__(self, name, value): 146 builtin = name.startswith("__") and name.endswith("__") 147 if not builtin and name not in self.__keylist__: 148 self.__keylist__.append(name) 149 self.__dict__[name] = value 150 151 def __delattr__(self, name): 152 try: 153 del self.__dict__[name] 154 builtin = name.startswith("__") and name.endswith("__") 155 if not builtin: 156 self.__keylist__.remove(name) 157 except Exception: 158 cls = self.__class__.__name__ 159 raise AttributeError("%s has no attribute '%s'" % (cls, name)) 160 161 def __getitem__(self, name): 162 if isinstance(name, int): 163 name = self.__keylist__[int(name)] 164 return getattr(self, name) 165 166 def __setitem__(self, name, value): 167 setattr(self, name, value) 168 169 def __iter__(self): 170 return Iter(self) 171 172 def __len__(self): 173 return len(self.__keylist__) 174 175 def __contains__(self, name): 176 return name in self.__keylist__ 177 178 def __repr__(self): 179 return str(self) 180 181 def __unicode__(self): 182 return self.__printer__.tostr(self) 183 184 185class Iter: 186 187 def __init__(self, sobject): 188 self.sobject = sobject 189 self.keylist = self.__keylist(sobject) 190 self.index = 0 191 192 def __next__(self): 193 keylist = self.keylist 194 nkeys = len(self.keylist) 195 while self.index < nkeys: 196 k = keylist[self.index] 197 self.index += 1 198 if hasattr(self.sobject, k): 199 v = getattr(self.sobject, k) 200 return (k, v) 201 raise StopIteration() 202 203 def __keylist(self, sobject): 204 keylist = sobject.__keylist__ 205 try: 206 keyset = set(keylist) 207 ordering = sobject.__metadata__.ordering 208 ordered = set(ordering) 209 if not ordered.issuperset(keyset): 210 log.debug("%s must be superset of %s, ordering ignored", 211 keylist, ordering) 212 raise KeyError() 213 return ordering 214 except Exception: 215 return keylist 216 217 def __iter__(self): 218 return self 219 220 221class Metadata(Object): 222 def __init__(self): 223 self.__keylist__ = [] 224 self.__printer__ = Printer() 225 226 227class Facade(Object): 228 def __init__(self, name): 229 Object.__init__(self) 230 md = self.__metadata__ 231 md.facade = name 232 233 234class Property(Object): 235 236 def __init__(self, value): 237 Object.__init__(self) 238 self.value = value 239 240 def items(self): 241 for item in self: 242 if item[0] != "value": 243 yield item 244 245 def get(self): 246 return self.value 247 248 def set(self, value): 249 self.value = value 250 return self 251 252 253class Printer: 254 """Pretty printing of a Object object.""" 255 256 @classmethod 257 def indent(cls, n): 258 return "%*s" % (n * 3, " ") 259 260 def tostr(self, object, indent=-2): 261 """Get s string representation of object.""" 262 history = [] 263 return self.process(object, history, indent) 264 265 def process(self, object, h, n=0, nl=False): 266 """Print object using the specified indent (n) and newline (nl).""" 267 if object is None: 268 return "None" 269 if isinstance(object, Object): 270 if len(object) == 0: 271 return "<empty>" 272 return self.print_object(object, h, n + 2, nl) 273 if isinstance(object, dict): 274 if len(object) == 0: 275 return "<empty>" 276 return self.print_dictionary(object, h, n + 2, nl) 277 if isinstance(object, (list, tuple)): 278 if len(object) == 0: 279 return "<empty>" 280 return self.print_collection(object, h, n + 2) 281 if isinstance(object, str): 282 return '"%s"' % (tostr(object),) 283 return "%s" % (tostr(object),) 284 285 def print_object(self, d, h, n, nl=False): 286 """Print complex using the specified indent (n) and newline (nl).""" 287 s = [] 288 cls = d.__class__ 289 if d in h: 290 s.append("(") 291 s.append(cls.__name__) 292 s.append(")") 293 s.append("...") 294 return "".join(s) 295 h.append(d) 296 if nl: 297 s.append("\n") 298 s.append(self.indent(n)) 299 if cls != Object: 300 s.append("(") 301 if isinstance(d, Facade): 302 s.append(d.__metadata__.facade) 303 else: 304 s.append(cls.__name__) 305 s.append(")") 306 s.append("{") 307 for item in d: 308 if self.exclude(d, item): 309 continue 310 item = self.unwrap(d, item) 311 s.append("\n") 312 s.append(self.indent(n+1)) 313 if isinstance(item[1], (list,tuple)): 314 s.append(item[0]) 315 s.append("[]") 316 else: 317 s.append(item[0]) 318 s.append(" = ") 319 s.append(self.process(item[1], h, n, True)) 320 s.append("\n") 321 s.append(self.indent(n)) 322 s.append("}") 323 h.pop() 324 return "".join(s) 325 326 def print_dictionary(self, d, h, n, nl=False): 327 """Print complex using the specified indent (n) and newline (nl).""" 328 if d in h: 329 return "{}..." 330 h.append(d) 331 s = [] 332 if nl: 333 s.append("\n") 334 s.append(self.indent(n)) 335 s.append("{") 336 for item in list(d.items()): 337 s.append("\n") 338 s.append(self.indent(n+1)) 339 if isinstance(item[1], (list,tuple)): 340 s.append(tostr(item[0])) 341 s.append("[]") 342 else: 343 s.append(tostr(item[0])) 344 s.append(" = ") 345 s.append(self.process(item[1], h, n, True)) 346 s.append("\n") 347 s.append(self.indent(n)) 348 s.append("}") 349 h.pop() 350 return "".join(s) 351 352 def print_collection(self, c, h, n): 353 """Print collection using the specified indent (n) and newline (nl).""" 354 if c in h: 355 return "[]..." 356 h.append(c) 357 s = [] 358 for item in c: 359 s.append("\n") 360 s.append(self.indent(n)) 361 s.append(self.process(item, h, n - 2)) 362 s.append(",") 363 h.pop() 364 return "".join(s) 365 366 def unwrap(self, d, item): 367 """Translate (unwrap) using an optional wrapper function.""" 368 try: 369 md = d.__metadata__ 370 pmd = getattr(md, "__print__", None) 371 if pmd is None: 372 return item 373 wrappers = getattr(pmd, "wrappers", {}) 374 fn = wrappers.get(item[0], lambda x: x) 375 return (item[0], fn(item[1])) 376 except Exception: 377 pass 378 return item 379 380 def exclude(self, d, item): 381 """Check metadata for excluded items.""" 382 try: 383 md = d.__metadata__ 384 pmd = getattr(md, "__print__", None) 385 if pmd is None: 386 return False 387 excludes = getattr(pmd, "excludes", []) 388 return item[0] in excludes 389 except Exception: 390 pass 391 return False 392