Package pywbem :: Module tupleparse
[frames] | no frames]

Source Code for Module pywbem.tupleparse

   1  # 
   2  # (C) Copyright 2003,2004 Hewlett-Packard Development Company, L.P. 
   3  # (C) Copyright 2006-2007 Novell, Inc. 
   4  # 
   5  # This library is free software; you can redistribute it and/or 
   6  # modify it under the terms of the GNU Lesser General Public 
   7  # License as published by the Free Software Foundation; either 
   8  # version 2.1 of the License, or (at your option) any later version. 
   9  # 
  10  # This program is distributed in the hope that it will be useful, but 
  11  # WITHOUT ANY WARRANTY; without even the implied warranty of 
  12  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
  13  # Lesser General Public License for more details. 
  14  # 
  15  # You should have received a copy of the GNU Lesser General Public 
  16  # License along with this program; if not, write to the Free Software 
  17  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  18  # 
  19  # Author: Martin Pool <mbp@hp.com> 
  20  # Author: Tim Potter <tpot@hp.com> 
  21  # Author: Bart Whiteley <bwhiteley@suse.de> 
  22  # 
  23   
  24  '''Tuple parser for the XML schema representing CIM messages. 
  25   
  26  This framework is meant to add some value to the tuple-tree 
  27  representation of CIM in XML by having the following properties: 
  28   
  29    - Silently ignoring whitespace text elements 
  30   
  31    - Conversion from tuple-tree representation into a python dictionary 
  32      which can then be accessed in a readable fashion. 
  33   
  34    - Validation of the XML elements and attributes without having to 
  35      use the DTD file or any external tools. 
  36   
  37  ''' 
  38   
  39  # Implementation: this works by a recursive descent down the CIM XML 
  40  # tupletree.  As we walk down, we produce cim_obj and cim_type 
  41  # objects representing the CIM message in digested form. 
  42   
  43  # For each XML node type FOO there is one function parse_foo, which 
  44  # returns the digested form by examining a tuple tree rooted at FOO. 
  45   
  46  # The resulting objects are constrained to the shape of the CIM XML 
  47  # tree: if one node in XML contains another, then the corresponding 
  48  # CIM object will contain the second.  However, there can be local 
  49  # transformations at each node: some levels are ommitted, some are 
  50  # transformed into lists or hashes. 
  51   
  52  # We try to validate that the tree is well-formed too.  The validation 
  53  # is more strict than the DTD, but it is forgiving of implementation 
  54  # quirks and bugs in Pegasus. 
  55   
  56  # Bear in mind in the parse functions that each tupletree tuple is 
  57  # structured as 
  58   
  59  #   tt[0]: name string             == name(tt) 
  60  #   tt[1]: hash of attributes      == attrs(tt) 
  61  #   tt[2]: sequence of children    == kids(tt) 
  62   
  63  # At the moment this layer is a little inconsistent: in some places it 
  64  # returns tupletrees, and in others Python objects.  It may be better 
  65  # to hide the tupletree/XML representation from higher level code. 
  66   
  67   
  68  # TODO: Maybe take a DTD fragment like "(DECLGROUP | 
  69  # DECLGROUP.WITHNAME | DECLGROUP.WITHPATH)*", parse that and check it 
  70  # directly. 
  71   
  72  # TODO: Syntax-check some attributes with defined formats, such as NAME 
  73   
  74  # TODO: Implement qualifiers by making subclasses of CIM types with a 
  75  # .qualifiers property. 
  76   
  77  # This module is meant to be safe for 'import *'. 
  78   
  79  import string 
  80  from types import StringTypes 
  81   
  82  from pywbem import cim_obj, tupletree 
  83  from pywbem.cim_obj import CIMInstance, CIMInstanceName, CIMClass, \ 
  84                             CIMClassName, CIMProperty, CIMMethod, \ 
  85                             CIMParameter, CIMQualifier, CIMQualifierDeclaration 
  86   
  87  __all__ = ['ParseError', 'parse_cim', 'parse_any'] 
  88   
89 -class ParseError(Exception):
90 """This exception is raised when there is a validation error detected 91 by the parser.""" 92 pass
93 94
95 -def filter_tuples(l):
96 """Return only the tuples in a list. 97 98 In a tupletree, tuples correspond to XML elements. Useful for 99 stripping out whitespace data in a child list.""" 100 101 if l is None: 102 return [] 103 else: 104 return [x for x in l if type(x) == tuple]
105 106
107 -def pcdata(tt):
108 """Return the concatenated character data within a tt. 109 110 The tt must not have non-character children.""" 111 for x in tt[2]: 112 if not isinstance(x, StringTypes): 113 raise ParseError, 'unexpected node %s under %s' % (`x`, `tt`) 114 return ''.join(tt[2])
115 116
117 -def name(tt):
118 return tt[0]
119 120
121 -def attrs(tt):
122 return tt[1]
123 124
125 -def kids(tt):
126 return filter_tuples(tt[2])
127 128
129 -def check_node(tt, nodename, required_attrs=[], optional_attrs=[], 130 allowed_children=None, 131 allow_pcdata=False):
132 """Check static local constraints on a single node. 133 134 The node must have the given name. The required attrs must be 135 present, and the optional attrs may be. 136 137 If allowed_children is not None, the node may have children of the 138 given types. It can be [] for nodes that may not have any 139 children. If it's None, it is assumed the children are validated 140 in some other way. 141 142 If allow_pcdata is true, then non-whitespace text children are allowed. 143 (Whitespace text nodes are always allowed.) 144 """ 145 146 if name(tt) <> nodename: 147 raise ParseError('expected node type %s, not %s' % 148 (nodename, name(tt))) 149 150 # Check we have all the required attributes, and no unexpected ones 151 tt_attrs = {} 152 if attrs(tt) is not None: 153 tt_attrs = attrs(tt).copy() 154 155 for attr in required_attrs: 156 if not tt_attrs.has_key(attr): 157 raise ParseError('expected %s attribute on %s node, but only ' 158 'have %s' % (attr, name(tt), attrs(tt).keys())) 159 del tt_attrs[attr] 160 161 for attr in optional_attrs: 162 if tt_attrs.has_key(attr): 163 del tt_attrs[attr] 164 165 if len(tt_attrs.keys()) > 0: 166 raise ParseError('invalid extra attributes %s' % tt_attrs.keys()) 167 168 if allowed_children is not None: 169 for c in kids(tt): 170 if name(c) not in allowed_children: 171 raise ParseError('unexpected node %s under %s; wanted %s' 172 % (name(c), name(tt), allowed_children)) 173 174 if not allow_pcdata: 175 for c in tt[2]: 176 if isinstance(c, StringTypes): 177 if c.lstrip(' \t\n') <> '': 178 raise ParseError('unexpected non-blank pcdata node %s ' 179 'under %s' % (`c`, name(tt)))
180 181
182 -def one_child(tt, acceptable):
183 """Parse children of a node with exactly one child node. 184 185 PCData is ignored. 186 """ 187 188 k = kids(tt) 189 190 if len(k) <> 1: 191 raise ParseError('In element %s with attributes %s, expected just '\ 192 'one child element %s, but got child elements %s' %\ 193 (name(tt), attrs(tt), acceptable, [t[0] for t in k])) 194 195 child = k[0] 196 197 if name(child) not in acceptable: 198 raise ParseError('In element %s with attributes %s, expected one '\ 199 'child element %s, but got child element %s' %\ 200 (name(tt), attrs(tt), acceptable, name(child))) 201 202 return parse_any(child)
203 204
205 -def optional_child(tt, allowed):
206 """Parse exactly zero or one of a list of elements from the 207 child nodes.""" 208 209 k = kids(tt) 210 211 if len(k) > 1: 212 raise ParseError('In element %s with attributes %s, expected zero or '\ 213 'one child element %s, but got child elements %s' %\ 214 (name(tt), attrs(tt), allowed, [t[0] for t in k])) 215 elif len(k) == 1: 216 return one_child(tt, allowed) 217 else: 218 return None
219 220
221 -def list_of_various(tt, acceptable):
222 """Parse zero or more of a list of elements from the child nodes. 223 224 Each element of the list can be any type from the list of acceptable 225 nodes.""" 226 227 r = [] 228 229 for child in kids(tt): 230 if name(child) not in acceptable: 231 raise ParseError('In element %s with attributes %s, expected zero '\ 232 'or more child elements %s, but got child element %s' %\ 233 (name(tt), attrs(tt), acceptable, name(child))) 234 r.append(parse_any(child)) 235 236 return r
237 238
239 -def list_of_matching(tt, matched):
240 """Parse only the children of particular types under tt. 241 242 Other children are ignored rather than giving an error.""" 243 244 r = [] 245 246 for child in kids(tt): 247 if name(child) not in matched: 248 continue 249 r.append(parse_any(child)) 250 251 return r
252 253
254 -def list_of_same(tt, acceptable):
255 """Parse a list of elements from child nodes. 256 257 The children can be any of the listed acceptable types, but they 258 must all be the same. 259 """ 260 261 k = kids(tt) 262 if not k: # empty list, consistent with list_of_various 263 return [] 264 265 w = name(k[0]) 266 if w not in acceptable: 267 raise ParseError('In element %s with attributes %s, expected child '\ 268 'elements %s, but got child element %s' %\ 269 (name(tt), attrs(tt), acceptable, w)) 270 r = [] 271 for child in k: 272 if name(child) <> w: 273 raise ParseError('In element %s with attributes %s, expected '\ 274 'sequence of only child elements %s, but got child '\ 275 'element %s' % (name(tt), attrs(tt), w, name(child))) 276 r.append(parse_any(child)) 277 278 return r
279 280
281 -def notimplemented(tt):
282 raise ParseError('parser for %s not implemented' % name(tt))
283 284 # 285 # Root element 286 # 287
288 -def parse_cim(tt):
289 """ 290 <!ELEMENT CIM (MESSAGE | DECLARATION)> 291 <!ATTLIST CIM 292 CIMVERSION CDATA #REQUIRED 293 DTDVERSION CDATA #REQUIRED> 294 """ 295 296 check_node(tt, 'CIM', ['CIMVERSION', 'DTDVERSION']) 297 298 if not attrs(tt)['CIMVERSION'].startswith('2.'): 299 raise ParseError('CIMVERSION is %s, expected 2.x.y' % 300 attrs(tt)['CIMVERSION']) 301 302 child = one_child(tt, ['MESSAGE', 'DECLARATION']) 303 304 return name(tt), attrs(tt), child
305 306 307 # 308 # Declaration elements 309 # 310
311 -def parse_declaration(tt):
312 """ 313 <!ELEMENT DECLARATION ( DECLGROUP | DECLGROUP.WITHNAME | 314 DECLGROUP.WITHPATH )+> 315 316 Note: We only support the DECLGROUP child, at this point. 317 """ 318 319 check_node(tt, 'DECLARATION') 320 321 child = one_child(tt, ['DECLGROUP']) 322 323 return name(tt), attrs(tt), child
324 325
326 -def parse_declgroup(tt):
327 """ 328 <!ELEMENT DECLGROUP ( (LOCALNAMESPACEPATH|NAMESPACEPATH)?, 329 QUALIFIER.DECLARATION*, VALUE.OBJECT* )> 330 331 Note: We only support the QUALIFIER.DECLARATION and VALUE.OBJECT 332 children, and with a multiplicity of 1, at this point. 333 """ 334 335 check_node(tt, 'DECLGROUP') 336 337 child = one_child(tt, ['QUALIFIER.DECLARATION', 'VALUE.OBJECT']) 338 339 return name(tt), attrs(tt), child
340 341 342 # 343 # Object value elements 344 # 345
346 -def parse_value(tt):
347 '''Return VALUE contents as a string''' 348 ## <!ELEMENT VALUE (#PCDATA)> 349 check_node(tt, 'VALUE', [], [], [], True) 350 351 return pcdata(tt)
352 353
354 -def parse_value_array(tt):
355 """Return list of strings.""" 356 ## <!ELEMENT VALUE.ARRAY (VALUE*)> 357 check_node(tt, 'VALUE.ARRAY', [], [], ['VALUE']) 358 359 return list_of_same(tt, ['VALUE'])
360 361
362 -def parse_value_reference(tt):
363 """ 364 <!ELEMENT VALUE.REFERENCE (CLASSPATH | LOCALCLASSPATH | CLASSNAME | 365 INSTANCEPATH | LOCALINSTANCEPATH | 366 INSTANCENAME)> 367 """ 368 369 check_node(tt, 'VALUE.REFERENCE', []) 370 371 child = one_child(tt, 372 ['CLASSPATH', 'LOCALCLASSPATH', 'CLASSNAME', 373 'INSTANCEPATH', 'LOCALINSTANCEPATH', 374 'INSTANCENAME']) 375 376 # The VALUE.REFERENCE wrapper element is discarded 377 return child
378 379
380 -def parse_value_refarray(tt):
381 """ 382 <!ELEMENT VALUE.REFARRAY (VALUE.REFERENCE*)> 383 """ 384 385 check_node(tt, 'VALUE.REFARRAY') 386 387 children = list_of_various(tt, ['VALUE.REFERENCE']) 388 389 # The VALUE.REFARRAY wrapper element is discarded 390 return children
391 392
393 -def parse_value_object(tt):
394 """ 395 <!ELEMENT VALUE.OBJECT (CLASS | INSTANCE)> 396 """ 397 398 check_node(tt, 'VALUE.OBJECT') 399 400 child = one_child(tt, ['CLASS', 'INSTANCE']) 401 402 return (name(tt), attrs(tt), child)
403 404
405 -def parse_value_namedinstance(tt):
406 """ 407 <!ELEMENT VALUE.NAMEDINSTANCE (INSTANCENAME, INSTANCE)> 408 """ 409 410 check_node(tt, 'VALUE.NAMEDINSTANCE') 411 412 k = kids(tt) 413 if len(k) <> 2: 414 raise ParseError('expecting (INSTANCENAME, INSTANCE), got %s' % `k`) 415 416 instancename = parse_instancename(k[0]) 417 instance = parse_instance(k[1]) 418 419 instance.path = instancename 420 421 return instance
422 423
424 -def parse_value_namedobject(tt):
425 """ 426 <!ELEMENT VALUE.NAMEDOBJECT (CLASS | (INSTANCENAME, INSTANCE))> 427 """ 428 429 check_node(tt, 'VALUE.NAMEDOBJECT') 430 431 k = kids(tt) 432 if len(k) == 1: 433 object = parse_class(k[0]) 434 elif len(k) == 2: 435 path = parse_instancename(kids(tt)[0]) 436 object = parse_instance(kids(tt)[1]) 437 438 object.path = path 439 else: 440 raise ParseError('Expecting one or two elements, got %s' % 441 `kids(tt)`) 442 443 return (name(tt), attrs(tt), object)
444 445
446 -def parse_value_objectwithlocalpath(tt):
447 """ 448 <!ELEMENT VALUE.OBJECTWITHLOCALPATH ((LOCALCLASSPATH, CLASS) | 449 (LOCALINSTANCEPATH, INSTANCE))> 450 """ 451 452 check_node(tt, 'VALUE.OBJECTWITHLOCALPATH') 453 454 if len(kids(tt)) != 2: 455 raise ParseError('Expecting two elements, got %s' % 456 len(kids(tt))); 457 458 if kids(tt)[0][0] == 'LOCALCLASSPATH': 459 object = (parse_localclasspath(kids(tt)[0]), 460 parse_class(kids(tt)[1])) 461 else: 462 path = parse_localinstancepath(kids(tt)[0]) 463 object = parse_instance(kids(tt)[1]) 464 465 object.path = path 466 467 return (name(tt), attrs(tt), object)
468
469 -def parse_value_objectwithpath(tt):
470 """ 471 <!ELEMENT VALUE.OBJECTWITHPATH ((CLASSPATH, CLASS) | 472 (INSTANCEPATH, INSTANCE))> 473 """ 474 475 check_node(tt, 'VALUE.OBJECTWITHPATH') 476 477 k = kids(tt) 478 479 if len(k) != 2: 480 raise ParseError('Expecting two elements, got %s' % k) 481 482 if name(k[0]) == 'CLASSPATH': 483 object = (parse_classpath(k[0]), 484 parse_class(k[1])) 485 else: 486 path = parse_instancepath(k[0]) 487 object = parse_instance(k[1]) 488 489 object.path = path 490 491 return (name(tt), attrs(tt), object)
492 493 # 494 # Object naming and locating elements 495 # 496
497 -def parse_namespacepath(tt):
498 """ 499 <!ELEMENT NAMESPACEPATH (HOST, LOCALNAMESPACEPATH)> 500 """ 501 502 check_node(tt, 'NAMESPACEPATH') 503 504 if len(kids(tt)) != 2: 505 raise ParseError('Expecting (HOST, LOCALNAMESPACEPATH) ' 506 'got %s' % kids(tt).keys()) 507 508 host = parse_host(kids(tt)[0]) 509 localnspath = parse_localnamespacepath(kids(tt)[1]) 510 511 return (host, localnspath)
512 513
514 -def parse_localnamespacepath(tt):
515 """ 516 <!ELEMENT LOCALNAMESPACEPATH (NAMESPACE+)> 517 """ 518 519 check_node(tt, 'LOCALNAMESPACEPATH', [], [], ['NAMESPACE']) 520 521 if len(kids(tt)) == 0: 522 raise ParseError('Expecting one or more of NAMESPACE, got nothing') 523 524 ns_list = list_of_various(tt, ['NAMESPACE']) 525 526 return string.join(ns_list, '/')
527 528
529 -def parse_host(tt):
530 """ 531 <!ELEMENT HOST (#PCDATA)> 532 """ 533 534 check_node(tt, 'HOST', allow_pcdata=True) 535 536 return pcdata(tt)
537 538
539 -def parse_namespace(tt):
540 """ 541 <!ELEMENT NAMESPACE EMPTY> 542 <!ATTLIST NAMESPACE 543 %CIMName;> 544 """ 545 546 check_node(tt, 'NAMESPACE', ['NAME'], [], []) 547 548 return attrs(tt)['NAME']
549 550
551 -def parse_classpath(tt):
552 """ 553 <!ELEMENT CLASSPATH (NAMESPACEPATH, CLASSNAME)> 554 """ 555 556 check_node(tt, 'CLASSPATH') 557 558 if len(kids(tt)) != 2: 559 raise ParseError('Expecting (NAMESPACEPATH, CLASSNAME) ' 560 'got %s' % kids(tt).keys()) 561 562 nspath = parse_namespacepath(kids(tt)[0]) 563 classname = parse_classname(kids(tt)[1]) 564 565 return CIMClassName(classname.classname, 566 host=nspath[0], namespace=nspath[1])
567 568
569 -def parse_localclasspath(tt):
570 """ 571 <!ELEMENT LOCALCLASSPATH (LOCALNAMESPACEPATH, CLASSNAME)> 572 """ 573 574 check_node(tt, 'LOCALCLASSPATH') 575 576 if len(kids(tt)) != 2: 577 raise ParseError('Expecting (LOCALNAMESPACEPATH, CLASSNAME) ' 578 'got %s' % kids(tt).keys()) 579 580 localnspath = parse_localnamespacepath(kids(tt)[0]) 581 classname = parse_classname(kids(tt)[1]) 582 583 return CIMClassName(classname.classname, namespace=localnspath)
584
585 -def parse_classname(tt):
586 """ 587 <!ELEMENT CLASSNAME EMPTY> 588 <!ATTLIST CLASSNAME 589 %CIMName;> 590 """ 591 check_node(tt, 'CLASSNAME', ['NAME'], [], []) 592 return CIMClassName(attrs(tt)['NAME'])
593 594
595 -def parse_instancepath(tt):
596 """ 597 <!ELEMENT INSTANCEPATH (NAMESPACEPATH, INSTANCENAME)> 598 """ 599 600 check_node(tt, 'INSTANCEPATH') 601 602 if len(kids(tt)) != 2: 603 raise ParseError('Expecting (NAMESPACEPATH, INSTANCENAME), got %s' 604 % `kids(tt)`) 605 606 nspath = parse_namespacepath(kids(tt)[0]) 607 instancename = parse_instancename(kids(tt)[1]) 608 609 instancename.host = nspath[0] 610 instancename.namespace = nspath[1] 611 612 return instancename
613
614 -def parse_localinstancepath(tt):
615 """ 616 <!ELEMENT LOCALINSTANCEPATH (LOCALNAMESPACEPATH, INSTANCENAME)> 617 """ 618 619 check_node(tt, 'LOCALINSTANCEPATH') 620 621 if len(kids(tt)) != 2: 622 raise ParseError('Expecting (LOCALNAMESPACEPATH, INSTANCENAME), ' 623 'got %s' % kids(tt).keys()) 624 625 localnspath = parse_localnamespacepath(kids(tt)[0]) 626 instancename = parse_instancename(kids(tt)[1]) 627 628 instancename.namespace = localnspath 629 630 return instancename
631
632 -def parse_instancename(tt):
633 """Parse XML INSTANCENAME into CIMInstanceName object.""" 634 635 ## <!ELEMENT INSTANCENAME (KEYBINDING* | KEYVALUE? | VALUE.REFERENCE?)> 636 ## <!ATTLIST INSTANCENAME %ClassName;> 637 638 check_node(tt, 'INSTANCENAME', ['CLASSNAME']) 639 640 if len(kids(tt)) == 0: 641 # probably not ever going to see this, but it's valid 642 # according to the grammar 643 return CIMInstanceName(attrs(tt)['CLASSNAME'], {}) 644 645 k0 = kids(tt)[0] 646 w = name(k0) 647 648 classname = attrs(tt)['CLASSNAME'] 649 650 if w == 'KEYVALUE' or w == 'VALUE.REFERENCE': 651 if len(kids(tt)) != 1: 652 raise ParseError('expected only one %s under %s' % 653 w, name(tt)) 654 655 # FIXME: This is probably not the best representation of these forms... 656 val = parse_any(k0) 657 return CIMInstanceName(classname, {None: val}) 658 elif w == 'KEYBINDING': 659 kbs = {} 660 for kb in list_of_various(tt, ['KEYBINDING']): 661 kbs.update(kb) 662 return CIMInstanceName(classname, kbs) 663 else: 664 raise ParseError('unexpected node %s under %s' % 665 (name(kids(tt)[0]), name(tt)))
666 667
668 -def parse_objectpath(tt):
669 """ 670 <!ELEMENT OBJECTPATH (INSTANCEPATH | CLASSPATH)> 671 """ 672 673 check_node(tt, 'OBJECTPATH') 674 675 child = one_child(tt, ['INSTANCEPATH', 'CLASSPATH']) 676 677 return (name(tt), attrs(tt), child)
678 679 680
681 -def parse_keybinding(tt):
682 ##<!ELEMENT KEYBINDING (KEYVALUE | VALUE.REFERENCE)> 683 ##<!ATTLIST KEYBINDING 684 ## %CIMName;> 685 686 """Returns one-item dictionary from name to Python value.""" 687 688 check_node(tt, 'KEYBINDING', ['NAME']) 689 690 child = one_child(tt, ['KEYVALUE', 'VALUE.REFERENCE']) 691 692 return {attrs(tt)['NAME']: child}
693 694
695 -def parse_keyvalue(tt):
696 ##<!ELEMENT KEYVALUE (#PCDATA)> 697 ##<!ATTLIST KEYVALUE 698 ## VALUETYPE (string | boolean | numeric) "string" 699 ## %CIMType; #IMPLIED> 700 701 702 """Parse VALUETYPE into Python primitive value""" 703 704 check_node(tt, 'KEYVALUE', ['VALUETYPE'], ['TYPE'], [], True) 705 706 p = pcdata(tt) 707 708 if not attrs(tt).has_key('VALUETYPE'): 709 return p; 710 711 vt = attrs(tt).get('VALUETYPE') 712 713 if vt == 'string': 714 return p 715 elif vt == 'boolean': 716 return unpack_boolean(p) 717 elif vt == 'numeric': 718 719 try: 720 # XXX: Use TYPE attribute to create named CIM type. 721 # if attrs(tt).has_key('TYPE'): 722 # return cim_obj.tocimobj(attrs(tt)['TYPE'], p.strip()) 723 724 # XXX: Would like to use long() here, but that tends to cause 725 # trouble when it's written back out as '2L' 726 return int(p.strip()) 727 except ValueError, e: 728 raise ParseError('invalid numeric %s under %s' % 729 (`p`, name(tt))) 730 else: 731 raise ParseError('invalid VALUETYPE %s in %s', 732 vt, name(tt))
733 734 735 # 736 # Object definition elements 737 # 738
739 -def parse_class(tt):
740 ## <!ELEMENT CLASS (QUALIFIER*, (PROPERTY | PROPERTY.ARRAY | 741 ## PROPERTY.REFERENCE)*, METHOD*)> 742 ## <!ATTLIST CLASS 743 ## %CIMName; 744 ## %SuperClass;> 745 746 # This doesn't check the ordering of elements, but it's not very important 747 check_node(tt, 'CLASS', ['NAME'], ['SUPERCLASS'], 748 ['QUALIFIER', 'PROPERTY', 'PROPERTY.REFERENCE', 749 'PROPERTY.ARRAY', 'METHOD']) 750 751 superclass = attrs(tt).get('SUPERCLASS') 752 753 # TODO: Return these as maps, not lists 754 properties = cim_obj.byname(list_of_matching(tt, ['PROPERTY', 755 'PROPERTY.REFERENCE', 756 'PROPERTY.ARRAY'])) 757 758 qualifiers = cim_obj.byname(list_of_matching(tt, ['QUALIFIER'])) 759 methods = cim_obj.byname(list_of_matching(tt, ['METHOD'])) 760 761 return CIMClass(attrs(tt)['NAME'], 762 superclass=superclass, 763 properties=properties, 764 qualifiers=qualifiers, 765 methods=methods)
766 767
768 -def parse_instance(tt):
769 """Return a CIMInstance. 770 771 The instance contains the properties, qualifiers and classname for 772 the instance""" 773 774 ##<!ELEMENT INSTANCE (QUALIFIER*, (PROPERTY | PROPERTY.ARRAY | 775 ## PROPERTY.REFERENCE)*)> 776 ##<!ATTLIST INSTANCE 777 ## %ClassName;> 778 779 check_node(tt, 'INSTANCE', ['CLASSNAME'], 780 ['QUALIFIER', 'PROPERTY', 'PROPERTY.ARRAY', 781 'PROPERTY.REFERENCE']) 782 783 ## XXX: This does not enforce ordering constraint 784 785 ## XXX: This does not enforce the constraint that there be only 786 ## one PROPERTY or PROPERTY.ARRAY. 787 788 ## TODO: Parse instance qualifiers 789 qualifiers = {} 790 props = list_of_matching(tt, ['PROPERTY.REFERENCE', 'PROPERTY', 791 'PROPERTY.ARRAY']) 792 793 obj = CIMInstance(attrs(tt)['CLASSNAME'], 794 qualifiers=qualifiers) 795 796 [obj.__setitem__(p.name, p) for p in props] 797 798 return obj
799
800 -def parse_scope(tt):
801 # <!ELEMENT SCOPE EMPTY> 802 # <!ATTLIST SCOPE 803 # CLASS (true | false) "false" 804 # ASSOCIATION (true | false) "false" 805 # REFERENCE (true | false) "false" 806 # PROPERTY (true | false) "false" 807 # METHOD (true | false) "false" 808 # PARAMETER (true | false) "false" 809 # INDICATION (true | false) "false" 810 check_node(tt, 'SCOPE', [], 811 ['CLASS', 'ASSOCIATION', 'REFERENCE', 'PROPERTY', 'METHOD', 812 'PARAMETER', 'INDICATION'], []) 813 return dict([(k, v.lower() == 'true') for k, v in attrs(tt).items()])
814
815 -def parse_qualifier_declaration(tt):
816 ## <!ELEMENT QUALIFIER.DECLARATION (SCOPE?, (VALUE | VALUE.ARRAY)?)> 817 ## <!ATTLIST QUALIFIER.DECLARATION 818 ## %CIMName; 819 ## %CIMType; #REQUIRED 820 ## ISARRAY (true|false) #IMPLIED 821 ## %ArraySize; 822 ## %QualifierFlavor;> 823 824 check_node(tt, 'QUALIFIER.DECLARATION', 825 ['NAME', 'TYPE'], 826 ['ISARRAY', 'ARRAYSIZE', 'OVERRIDABLE', 'TOSUBCLASS', 827 'TOINSTANCE', 'TRANSLATABLE'], 828 ['SCOPE', 'VALUE', 'VALUE.ARRAY']) 829 830 a = attrs(tt) 831 qname = a['NAME'] 832 type = a['TYPE'] 833 try: 834 is_array = a['ISARRAY'].lower() == 'true' 835 except KeyError: 836 is_array = False 837 try: 838 array_size = int(a['ARRAYSIZE']) 839 except KeyError: 840 array_size = None 841 842 flavors = {} 843 for f in ['OVERRIDABLE', 'TOSUBCLASS', 'TOINSTANCE', 'TRANSLATABLE']: 844 try: 845 flavors[f.lower()] = a[f].lower() == 'true' 846 except KeyError: 847 pass 848 849 scopes = None 850 value = None 851 for child in kids(tt): 852 if name(child) == 'SCOPE': 853 if scopes is not None: 854 raise ParseError("Multiple SCOPE tags encountered") 855 scopes = parse_any(child) 856 else: 857 if value is not None: 858 raise ParseError("Multiple VALUE/VALUE.ARRAY tags encountered") 859 value = cim_obj.tocimobj(type, parse_any(child)) 860 861 return CIMQualifierDeclaration(qname, type, value, is_array, 862 array_size, scopes, **flavors)
863 864
865 -def parse_qualifier(tt):
866 ## <!ELEMENT QUALIFIER (VALUE | VALUE.ARRAY)> 867 ## <!ATTLIST QUALIFIER %CIMName; 868 ## %CIMType; #REQUIRED 869 ## %Propagated; 870 ## %QualifierFlavor;> 871 872 check_node(tt, 'QUALIFIER', ['NAME', 'TYPE'], 873 ['OVERRIDABLE', 'TOSUBCLASS', 'TOINSTANCE', 874 'TRANSLATABLE', 'PROPAGATED'], 875 ['VALUE', 'VALUE.ARRAY']) 876 877 a = attrs(tt) 878 879 q = CIMQualifier(a['NAME'], unpack_value(tt), type=a['TYPE']) 880 881 ## TODO: Lift this out? 882 for i in ['OVERRIDABLE', 'TOSUBCLASS', 'TOINSTANCE', 883 'TRANSLATABLE', 'PROPAGATED']: 884 rv = a.get(i) 885 if rv not in ['true', 'false', None]: 886 raise ParseError("invalid value %s for %s on %s" % 887 (`rv`, i, name(tt))) 888 if rv == 'true': 889 rv = True 890 elif rv == 'false': 891 rv = False 892 893 setattr(q, i.lower(), rv) 894 895 return q
896 897
898 -def parse_property(tt):
899 """Parse PROPERTY into a CIMProperty object. 900 901 VAL is just the pcdata of the enclosed VALUE node.""" 902 903 ## <!ELEMENT PROPERTY (QUALIFIER*, VALUE?)> 904 ## <!ATTLIST PROPERTY %CIMName; 905 ## %ClassOrigin; 906 ## %Propagated; 907 ## %CIMType; #REQUIRED> 908 909 ## TODO: Parse this into NAME, VALUE, where the value contains 910 ## magic fields for the qualifiers and the propagated flag. 911 912 check_node(tt, 'PROPERTY', ['TYPE', 'NAME'], 913 ['NAME', 'CLASSORIGIN', 'PROPAGATED', 'EmbeddedObject', 914 'EMBEDDEDOBJECT'], 915 ['QUALIFIER', 'VALUE']) 916 917 quals = {} 918 for q in list_of_matching(tt, ['QUALIFIER']): 919 quals[q.name] = q 920 921 a = attrs(tt) 922 try: 923 val = unpack_value(tt) 924 except ValueError as exc: 925 msg = str(exc) 926 raise ParseError('Cannot parse value for property "%s": %s' %\ 927 (a['NAME'], msg)) 928 929 embedded_object = None 930 if 'EmbeddedObject' in a or 'EMBEDDEDOBJECT' in a: 931 try: 932 embedded_object = a['EmbeddedObject'] 933 except KeyError: 934 embedded_object = a['EMBEDDEDOBJECT'] 935 if embedded_object is not None: 936 val = parse_embeddedObject(val) 937 938 return CIMProperty(a['NAME'], 939 val, 940 a['TYPE'], 941 class_origin=a.get('CLASSORIGIN'), 942 propagated=unpack_boolean(a.get('PROPAGATED')), 943 qualifiers=quals, 944 embedded_object=embedded_object)
945 946
947 -def parse_property_array(tt):
948 """ 949 <!ELEMENT PROPERTY.ARRAY (QUALIFIER*, VALUE.ARRAY?)> 950 <!ATTLIST PROPERTY.ARRAY %CIMName; 951 %CIMType; #REQUIRED 952 %ArraySize; 953 %ClassOrigin; 954 %Propagated;> 955 """ 956 957 check_node(tt, 'PROPERTY.ARRAY', ['NAME', 'TYPE'], 958 ['REFERENCECLASS', 'CLASSORIGIN', 'PROPAGATED', 959 'ARRAYSIZE', 'EmbeddedObject', 'EMBEDDEDOBJECT'], 960 ['QUALIFIER', 'VALUE.ARRAY']) 961 962 quals = {} 963 for q in list_of_matching(tt, ['QUALIFIER']): 964 quals[q.name] = q 965 966 values = unpack_value(tt) 967 a = attrs(tt) 968 embedded_object = None 969 if 'EmbeddedObject' in a or 'EMBEDDEDOBJECT' in a: 970 try: 971 embedded_object = a['EmbeddedObject'] 972 except KeyError: 973 embedded_object = a['EMBEDDEDOBJECT'] 974 975 if embedded_object is not None: 976 values = parse_embeddedObject(values) 977 978 obj = CIMProperty(a['NAME'], 979 values, 980 a['TYPE'], 981 class_origin=a.get('CLASSORIGIN'), 982 qualifiers=quals, 983 is_array=True, 984 embedded_object=embedded_object) 985 986 ## TODO: qualifiers, other attributes 987 return obj
988 989
990 -def parse_property_reference(tt):
991 """ 992 <!ELEMENT PROPERTY.REFERENCE (QUALIFIER*, (VALUE.REFERENCE)?)> 993 <!ATTLIST PROPERTY.REFERENCE 994 %CIMName; 995 %ReferenceClass; 996 %ClassOrigin; 997 %Propagated;> 998 """ 999 1000 check_node(tt, 'PROPERTY.REFERENCE', ['NAME'], 1001 ['REFERENCECLASS', 'CLASSORIGIN', 'PROPAGATED']) 1002 1003 value = list_of_matching(tt, ['VALUE.REFERENCE']) 1004 1005 if value is None or len(value) == 0: 1006 value = None 1007 elif len(value) == 1: 1008 value = value[0] 1009 else: 1010 raise ParseError('Too many VALUE.REFERENCE elements.') 1011 1012 attributes = attrs(tt) 1013 pref = CIMProperty(attributes['NAME'], value, type='reference') 1014 1015 for q in list_of_matching(tt, ['QUALIFIER']): 1016 pref.qualifiers[q.name] = q 1017 1018 if attributes.has_key('REFERENCECLASS'): 1019 pref.reference_class = attributes['REFERENCECLASS'] 1020 1021 if attributes.has_key('CLASSORIGIN'): 1022 pref.class_origin = attributes['CLASSORIGIN'] 1023 1024 if attributes.has_key('PROPAGATED'): 1025 pref.propagated = attributes['PROPAGATED'] 1026 1027 return pref
1028 1029
1030 -def parse_method(tt):
1031 """ 1032 <!ELEMENT METHOD (QUALIFIER*, (PARAMETER | PARAMETER.REFERENCE | 1033 PARAMETER.ARRAY | PARAMETER.REFARRAY)*)> 1034 <!ATTLIST METHOD %CIMName; 1035 %CIMType; #IMPLIED 1036 %ClassOrigin; 1037 %Propagated;> 1038 """ 1039 1040 check_node(tt, 'METHOD', ['NAME'], 1041 ['TYPE', 'CLASSORIGIN', 'PROPAGATED'], 1042 ['QUALIFIER', 'PARAMETER', 'PARAMETER.REFERENCE', 1043 'PARAMETER.ARRAY', 'PARAMETER.REFARRAY']) 1044 1045 qualifiers = cim_obj.byname(list_of_matching(tt, ['QUALIFIER'])) 1046 1047 parameters = cim_obj.byname(list_of_matching(tt, ['PARAMETER', 1048 'PARAMETER.REFERENCE', 1049 'PARAMETER.ARRAY', 1050 'PARAMETER.REFARRAY',])) 1051 1052 a = attrs(tt) 1053 1054 return CIMMethod(a['NAME'], 1055 return_type=a.get('TYPE'), 1056 parameters=parameters, 1057 qualifiers=qualifiers, 1058 class_origin=a.get('CLASSORIGIN'), 1059 propagated=unpack_boolean(a.get('PROPAGATED')))
1060 1061
1062 -def parse_parameter(tt):
1063 """ 1064 <!ELEMENT PARAMETER (QUALIFIER*)> 1065 <!ATTLIST PARAMETER 1066 %CIMName; 1067 %CIMType; #REQUIRED> 1068 """ 1069 1070 check_node(tt, 'PARAMETER', ['NAME', 'TYPE'], []) 1071 1072 quals = {} 1073 for q in list_of_matching(tt, ['QUALIFIER']): 1074 quals[q.name] = q 1075 1076 a = attrs(tt) 1077 1078 return CIMParameter(a['NAME'], type=a['TYPE'], qualifiers=quals)
1079
1080 -def parse_parameter_reference(tt):
1081 """ 1082 <!ELEMENT PARAMETER.REFERENCE (QUALIFIER*)> 1083 <!ATTLIST PARAMETER.REFERENCE 1084 %CIMName; 1085 %ReferenceClass;> 1086 """ 1087 1088 check_node(tt, 'PARAMETER.REFERENCE', ['NAME'], ['REFERENCECLASS']) 1089 1090 quals = {} 1091 for q in list_of_matching(tt, ['QUALIFIER']): 1092 quals[q.name] = q 1093 1094 a = attrs(tt) 1095 1096 return CIMParameter(a['NAME'], 1097 type='reference', 1098 reference_class=a.get('REFERENCECLASS'), 1099 qualifiers=quals)
1100 1101
1102 -def parse_parameter_array(tt):
1103 """ 1104 <!ELEMENT PARAMETER.ARRAY (QUALIFIER*)> 1105 <!ATTLIST PARAMETER.ARRAY 1106 %CIMName; 1107 %CIMType; #REQUIRED 1108 %ArraySize;> 1109 """ 1110 1111 check_node(tt, 'PARAMETER.ARRAY', ['NAME', 'TYPE'], 1112 ['ARRAYSIZE']) 1113 1114 quals = {} 1115 for q in list_of_matching(tt, ['QUALIFIER']): 1116 quals[q.name] = q 1117 1118 a = attrs(tt) 1119 1120 array_size = a.get('ARRAYSIZE') 1121 if array_size is not None: 1122 array_size = int(array_size) 1123 1124 return CIMParameter(a['NAME'], 1125 type=a['TYPE'], 1126 is_array=True, 1127 array_size=array_size, 1128 qualifiers=quals)
1129 1130
1131 -def parse_parameter_refarray(tt):
1132 """ 1133 <!ELEMENT PARAMETER.REFARRAY (QUALIFIER*)> 1134 <!ATTLIST PARAMETER.REFARRAY 1135 %CIMName; 1136 %ReferenceClass; 1137 %ArraySize;> 1138 """ 1139 1140 check_node(tt, 'PARAMETER.REFARRAY', ['NAME'], 1141 ['REFERENCECLASS', 'ARRAYSIZE']) 1142 1143 quals = {} 1144 for q in list_of_matching(tt, ['QUALIFIER']): 1145 quals[q.name] = q 1146 1147 a = attrs(tt) 1148 1149 array_size = a.get('ARRAYSIZE') 1150 if array_size is not None: 1151 array_size = int(array_size) 1152 1153 return CIMParameter(a['NAME'], 'reference', 1154 is_array=True, 1155 reference_class=a.get('REFERENCECLASS'), 1156 array_size=array_size, 1157 qualifiers=quals)
1158 1159 1160 # 1161 # Message elements 1162 # 1163
1164 -def parse_message(tt):
1165 """ 1166 <!ELEMENT MESSAGE (SIMPLEREQ | MULTIREQ | SIMPLERSP | MULTIRSP)> 1167 <!ATTLIST MESSAGE 1168 ID CDATA #REQUIRED 1169 PROTOCOLVERSION CDATA #REQUIRED> 1170 """ 1171 1172 check_node(tt, 'MESSAGE', ['ID', 'PROTOCOLVERSION']) 1173 1174 messages = one_child( 1175 tt, ['SIMPLEREQ', 'MULTIREQ', 'SIMPLERSP', 'MULTIRSP', 'SIMPLEEXPREQ']) 1176 1177 if type(messages) is not list: 1178 # make single and multi forms consistent 1179 messages = [messages] 1180 1181 return name(tt), attrs(tt), messages
1182 1183
1184 -def parse_multireq(tt):
1185 raise ParseError('MULTIREQ parser not implemented')
1186 1187
1188 -def parse_multiexpreq(tt):
1189 raise ParseError('MULTIEXPREQ parser not implemented')
1190
1191 -def parse_simpleexpreq(tt):
1192 """ 1193 <!ELEMENT SIMPLEEXPREQ (EXPMETHODCALL)> 1194 """ 1195 1196 child = one_child(tt, ['EXPMETHODCALL']) 1197 1198 return name(tt), attrs(tt), child
1199
1200 -def parse_simplereq(tt):
1201 """ 1202 <!ELEMENT SIMPLEREQ (IMETHODCALL | METHODCALL)> 1203 """ 1204 1205 check_node(tt, 'SIMPLEREQ') 1206 1207 child = one_child(tt, ['IMETHODCALL', 'METHODCALL']) 1208 1209 return name(tt), attrs(tt), child
1210 1211
1212 -def parse_imethodcall(tt):
1213 """ 1214 <!ELEMENT IMETHODCALL (LOCALNAMESPACEPATH, IPARAMVALUE*)> 1215 <!ATTLIST IMETHODCALL 1216 %CIMName;> 1217 """ 1218 1219 check_node(tt, 'IMETHODCALL', ['NAME']) 1220 1221 if len(kids(tt)) < 1: 1222 raise ParseError('Expecting LOCALNAMESPACEPATH, got nothing') 1223 1224 localnspath = parse_localnamespacepath(kids(tt)[0]) 1225 1226 params = map(lambda x: parse_iparamvalue(x), 1227 kids(tt)[1:]) 1228 1229 return (name(tt), attrs(tt), localnspath, params)
1230 1231
1232 -def parse_methodcall(tt):
1233 """ 1234 <!ELEMENT METHODCALL ((LOCALCLASSPATH|LOCALINSTANCEPATH),PARAMVALUE*)> 1235 <!ATTLIST METHODCALL 1236 %CIMName;> 1237 """ 1238 1239 check_node(tt, 'METHODCALL', ['NAME'], [], 1240 ['LOCALCLASSPATH', 'LOCALINSTANCEPATH', 'PARAMVALUE']) 1241 path = list_of_matching(tt, ['LOCALCLASSPATH', 'LOCALINSTANCEPATH']) 1242 if len(path) != 1: 1243 raise ParseError('Expecting one of LOCALCLASSPATH or ' \ 1244 'LOCALINSTANCEPATH, got %s' % `path`) 1245 path = path[0] 1246 params = list_of_matching(tt, ['PARAMVALUE']) 1247 return (name(tt), attrs(tt), path, params)
1248 1249
1250 -def parse_expmethodcall(tt):
1251 """ 1252 <!ELEMENT EXPMETHODCALL (EXPPARAMVALUE*)> 1253 <!ATTLIST EXPMETHODCALL 1254 %CIMName;> 1255 """ 1256 1257 check_node(tt, 'EXPMETHODCALL', ['NAME'], [], ['EXPPARAMVALUE']) 1258 1259 1260 params = list_of_matching(tt, ['EXPPARAMVALUE']) 1261 1262 return (name(tt), attrs(tt), params)
1263 1264
1265 -def parse_paramvalue(tt):
1266 ## <!ELEMENT PARAMVALUE (VALUE | VALUE.REFERENCE | VALUE.ARRAY | 1267 ## VALUE.REFARRAY)?> 1268 ## <!ATTLIST PARAMVALUE 1269 ## %CIMName; 1270 ## %ParamType; #IMPLIED 1271 ## %EmbeddedObject;> 1272 1273 ## Version 2.1.1 of the DTD lacks the %ParamType attribute but it 1274 ## is present in version 2.2. Make it optional to be backwards 1275 ## compatible. 1276 1277 check_node(tt, 'PARAMVALUE', ['NAME'], 1278 ['PARAMTYPE', 'EmbeddedObject', 'EMBEDDEDOBJECT']) 1279 1280 child = optional_child(tt, 1281 ['VALUE', 'VALUE.REFERENCE', 'VALUE.ARRAY', 1282 'VALUE.REFARRAY',]) 1283 1284 if attrs(tt).has_key('PARAMTYPE'): 1285 paramtype = attrs(tt)['PARAMTYPE'] 1286 else: 1287 paramtype = None 1288 1289 if 'EmbeddedObject' in attrs(tt) or 'EMBEDDEDOBJECT' in attrs(tt): 1290 child = parse_embeddedObject(child) 1291 1292 return attrs(tt)['NAME'], paramtype, child
1293 1294
1295 -def parse_iparamvalue(tt):
1296 ## <!ELEMENT IPARAMVALUE (VALUE | VALUE.ARRAY | VALUE.REFERENCE | 1297 ## INSTANCENAME | CLASSNAME | QUALIFIER.DECLARATION | 1298 ## CLASS | INSTANCE | VALUE.NAMEDINSTANCE)?> 1299 ## <!ATTLIST IPARAMVALUE %CIMName;> 1300 1301 """Returns NAME, VALUE pair.""" 1302 1303 check_node(tt, 'IPARAMVALUE', ['NAME'], []) 1304 1305 child = optional_child(tt, 1306 ['VALUE', 'VALUE.ARRAY', 'VALUE.REFERENCE', 1307 'INSTANCENAME', 'CLASSNAME', 1308 'QUALIFIER.DECLARATION', 'CLASS', 'INSTANCE', 1309 'VALUE.NAMEDINSTANCE']) 1310 1311 name = attrs(tt)['NAME'] 1312 if isinstance(child, basestring) and \ 1313 name.lower() in ['deepinheritance', 'localonly', 1314 'includequalifiers', 'includeclassorigin']: 1315 if child.lower() in ['true', 'false']: 1316 child = child.lower() == 'true' 1317 1318 return name, child
1319 1320
1321 -def parse_expparamvalue(tt):
1322 """ 1323 <!ELEMENT EXPPARAMVALUE (INSTANCE?)> 1324 <!ATTLIST EXPPARAMVALUE 1325 %CIMName;> 1326 """ 1327 1328 check_node(tt, 'EXPPARAMVALUE', ['NAME'], [], ['INSTANCE']) 1329 1330 child = optional_child(tt, ['INSTANCE']) 1331 1332 name = attrs(tt)['NAME'] 1333 return name, child
1334 1335
1336 -def parse_multirsp(tt):
1337 raise ParseError('MULTIRSP parser not implemented')
1338 1339
1340 -def parse_multiexprsp(tt):
1341 raise ParseError('MULTIEXPRSP parser not implemented')
1342 1343
1344 -def parse_simplersp(tt):
1345 ## <!ELEMENT SIMPLERSP (METHODRESPONSE | IMETHODRESPONSE)> 1346 check_node(tt, 'SIMPLERSP', [], []) 1347 1348 child = one_child(tt, ['METHODRESPONSE', 'IMETHODRESPONSE']) 1349 1350 return name(tt), attrs(tt), child
1351 1352
1353 -def parse_simpleexprsp(tt):
1354 raise ParseError('SIMPLEEXPRSP parser not implemented')
1355 1356
1357 -def parse_methodresponse(tt):
1358 ## <!ELEMENT METHODRESPONSE (ERROR | (RETURNVALUE?, PARAMVALUE*))> 1359 ## <!ATTLIST METHODRESPONSE 1360 ## %CIMName;> 1361 1362 check_node(tt, 'METHODRESPONSE', ['NAME'], []) 1363 1364 return name(tt), attrs(tt), list_of_various(tt, ['ERROR', 'RETURNVALUE', 1365 'PARAMVALUE'])
1366 1367
1368 -def parse_expmethodresponse(tt):
1369 raise ParseError('EXPMETHODRESPONSE parser not implemented')
1370 1371
1372 -def parse_imethodresponse(tt):
1373 ## <!ELEMENT IMETHODRESPONSE (ERROR | IRETURNVALUE?)> 1374 ## <!ATTLIST IMETHODRESPONSE %CIMName;> 1375 check_node(tt, 'IMETHODRESPONSE', ['NAME'], []) 1376 1377 return name(tt), attrs(tt), optional_child(tt, ['ERROR', 'IRETURNVALUE'])
1378 1379
1380 -def parse_error(tt):
1381 """ 1382 <!ELEMENT ERROR EMPTY> 1383 <!ATTLIST ERROR 1384 CODE CDATA #REQUIRED 1385 DESCRIPTION CDATA #IMPLIED> 1386 """ 1387 1388 ## TODO: Return a CIMError object, not a tuple 1389 1390 check_node(tt, 'ERROR', ['CODE'], ['DESCRIPTION']) 1391 1392 return (name(tt), attrs(tt), None)
1393 1394
1395 -def parse_returnvalue(tt):
1396 ## <!ELEMENT RETURNVALUE (VALUE | VALUE.ARRAY | VALUE.REFERENCE | 1397 ## VALUE.REFARRAY)> 1398 ## <!ATTLIST RETURNVALUE %ParamType; #IMPLIED> 1399 1400 ## Version 2.1.1 of the DTD lacks the %ParamType attribute but it 1401 ## is present in version 2.2. Make it optional to be backwards 1402 ## compatible. 1403 1404 check_node(tt, 'RETURNVALUE', [], ['PARAMTYPE']) 1405 1406 return name(tt), attrs(tt), one_child(tt, ['VALUE', 'VALUE.ARRAY', 1407 'VALUE.REFERENCE', 1408 'VALUE.REFARRAY'])
1409 1410
1411 -def parse_ireturnvalue(tt):
1412 ## <!ELEMENT IRETURNVALUE (CLASSNAME* | INSTANCENAME* | VALUE* | 1413 ## VALUE.OBJECTWITHPATH* | 1414 ## VALUE.OBJECTWITHLOCALPATH* | VALUE.OBJECT* | 1415 ## OBJECTPATH* | QUALIFIER.DECLARATION* | 1416 ## VALUE.ARRAY? | VALUE.REFERENCE? | CLASS* | 1417 ## INSTANCE* | VALUE.NAMEDINSTANCE*)> 1418 1419 check_node(tt, 'IRETURNVALUE', [], []) 1420 1421 # XXX: doesn't prohibit the case of only one VALUE.ARRAY or 1422 # VALUE.REFERENCE. But why is that required? Why can it return 1423 # multiple VALUEs but not multiple VALUE.REFERENCEs? 1424 1425 values = list_of_same(tt, ['CLASSNAME', 'INSTANCENAME', 1426 'VALUE', 'VALUE.OBJECTWITHPATH', 'VALUE.OBJECT', 1427 'OBJECTPATH', 'QUALIFIER.DECLARATION', 1428 'VALUE.ARRAY', 'VALUE.REFERENCE', 1429 'CLASS', 'INSTANCE', 1430 'VALUE.NAMEDINSTANCE',]) 1431 1432 ## TODO: Call unpack_value if appropriate 1433 1434 return name(tt), attrs(tt), values
1435 1436 # 1437 # Object naming and locating elements 1438 # 1439
1440 -def parse_any(tt):
1441 """Parse any fragment of XML.""" 1442 1443 nodename = name(tt).lower().replace('.', '_') 1444 fn_name = 'parse_' + nodename 1445 fn = globals().get(fn_name) 1446 if fn is None: 1447 raise ParseError('no parser for node type %s' % name(tt)) 1448 else: 1449 return fn(tt)
1450
1451 -def parse_embeddedObject(val):
1452 if isinstance(val, list): 1453 return [parse_embeddedObject(obj) for obj in val] 1454 if val is None: 1455 return None 1456 tt = tupletree.xml_to_tupletree(val) 1457 if tt[0] == 'INSTANCE': 1458 return parse_instance(tt) 1459 elif tt[0] == 'CLASS': 1460 return parse_class(tt) 1461 else: 1462 raise ParseError('Error parsing embedded object')
1463 1464
1465 -def unpack_value(tt):
1466 """Find VALUE or VALUE.ARRAY under TT and convert to a Python value. 1467 1468 Looks at the TYPE of the node to work out how to decode it. 1469 Handles nodes with no value (e.g. in CLASS.) 1470 """ 1471 ## TODO: Handle VALUE.REFERENCE, VALUE.REFARRAY 1472 1473 valtype = attrs(tt)['TYPE'] 1474 1475 raw_val = list_of_matching(tt, ['VALUE', 'VALUE.ARRAY']) 1476 if len(raw_val) == 0: 1477 return None 1478 elif len(raw_val) > 1: 1479 raise ParseError('more than one VALUE or VALUE.ARRAY under %s' % \ 1480 name(tt)) 1481 1482 raw_val = raw_val[0] 1483 1484 if isinstance(raw_val, list): 1485 return [cim_obj.tocimobj(valtype, x) for x in raw_val] 1486 elif len(raw_val) == 0 and valtype != 'string': 1487 return None 1488 else: 1489 return cim_obj.tocimobj(valtype, raw_val)
1490 1491
1492 -def unpack_boolean(p):
1493 """Unpack a boolean, represented as "TRUE" or "FALSE" in CIM.""" 1494 if p is None: 1495 return None 1496 1497 ## CIM-XML says "These values MUST be treated as case-insensitive" 1498 ## (even though the XML definition requires them to be lowercase.) 1499 1500 p = p.strip().lower() # ignore space 1501 if p == 'true': 1502 return True 1503 elif p == 'false': 1504 return False 1505 elif p == '': 1506 return None 1507 else: 1508 raise ParseError('invalid boolean %s' % `p`)
1509