Package pyffi :: Package object_models :: Package xml
[hide private]
[frames] | no frames]

Source Code for Package pyffi.object_models.xml

  1  """Format classes and metaclasses for binary file formats described by an xml 
  2  file, and xml handler for converting the xml description into Python classes. 
  3  """ 
  4   
  5  # ***** BEGIN LICENSE BLOCK ***** 
  6  # 
  7  # Copyright (c) 2007-2011, Python File Format Interface 
  8  # All rights reserved. 
  9  # 
 10  # Redistribution and use in source and binary forms, with or without 
 11  # modification, are permitted provided that the following conditions 
 12  # are met: 
 13  # 
 14  #    * Redistributions of source code must retain the above copyright 
 15  #      notice, this list of conditions and the following disclaimer. 
 16  # 
 17  #    * Redistributions in binary form must reproduce the above 
 18  #      copyright notice, this list of conditions and the following 
 19  #      disclaimer in the documentation and/or other materials provided 
 20  #      with the distribution. 
 21  # 
 22  #    * Neither the name of the Python File Format Interface 
 23  #      project nor the names of its contributors may be used to endorse 
 24  #      or promote products derived from this software without specific 
 25  #      prior written permission. 
 26  # 
 27  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
 28  # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
 29  # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 
 30  # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 
 31  # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 
 32  # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 
 33  # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
 34  # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
 35  # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 36  # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 
 37  # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
 38  # POSSIBILITY OF SUCH DAMAGE. 
 39  # 
 40  # ***** END LICENSE BLOCK ***** 
 41   
 42  import logging 
 43  import time # for timing stuff 
 44  import types 
 45  import os.path 
 46  import sys 
 47  import xml.sax 
 48   
 49  import pyffi.object_models 
 50  from pyffi.object_models.xml.struct_    import StructBase 
 51  from pyffi.object_models.xml.basic      import BasicBase 
 52  from pyffi.object_models.xml.bit_struct import BitStructBase 
 53  from pyffi.object_models.xml.enum       import EnumBase 
 54  from pyffi.object_models.xml.expression import Expression 
55 56 57 -class MetaFileFormat(pyffi.object_models.MetaFileFormat):
58 """The MetaFileFormat metaclass transforms the XML description 59 of a file format into a bunch of classes which can be directly 60 used to manipulate files in this format. 61 62 The actual implementation of the parser is delegated to 63 pyffi.object_models.xml.FileFormat. 64 """ 65
66 - def __init__(cls, name, bases, dct):
67 """This function constitutes the core of the class generation 68 process. For instance, we declare NifFormat to have metaclass 69 MetaFileFormat, so upon creation of the NifFormat class, 70 the __init__ function is called, with 71 72 :param cls: The class created using MetaFileFormat, for example 73 NifFormat. 74 :param name: The name of the class, for example 'NifFormat'. 75 :param bases: The base classes, usually (object,). 76 :param dct: A dictionary of class attributes, such as 'xml_file_name'. 77 """ 78 79 super(MetaFileFormat, cls).__init__(name, bases, dct) 80 81 # preparation: make deep copy of lists of enums, structs, etc. 82 cls.xml_enum = cls.xml_enum[:] 83 cls.xml_alias = cls.xml_alias[:] 84 cls.xml_bit_struct = cls.xml_bit_struct[:] 85 cls.xml_struct = cls.xml_struct[:] 86 87 # parse XML 88 89 # we check dct to avoid parsing the same file more than once in 90 # the hierarchy 91 xml_file_name = dct.get('xml_file_name') 92 if xml_file_name: 93 # set up XML parser 94 parser = xml.sax.make_parser() 95 parser.setContentHandler(XmlSaxHandler(cls, name, bases, dct)) 96 97 # open XML file 98 xml_file = cls.openfile(xml_file_name, cls.xml_file_path) 99 100 # parse the XML file: control is now passed on to XmlSaxHandler 101 # which takes care of the class creation 102 cls.logger.debug("Parsing %s and generating classes." 103 % xml_file_name) 104 start = time.clock() 105 try: 106 parser.parse(xml_file) 107 finally: 108 xml_file.close() 109 cls.logger.debug("Parsing finished in %.3f seconds." 110 % (time.clock() - start))
111
112 113 -class FileFormat(pyffi.object_models.FileFormat):
114 """This class can be used as a base class for file formats 115 described by an xml file.""" 116 __metaclass__ = MetaFileFormat 117 xml_file_name = None #: Override. 118 xml_file_path = None #: Override. 119 logger = logging.getLogger("pyffi.object_models.xml") 120 121 # We also keep an ordered list of all classes that have been created. 122 # The xml_struct list includes all xml generated struct classes, 123 # including those that are replaced by a native class in cls (for 124 # instance NifFormat.String). The idea is that these lists should 125 # contain sufficient info from the xml so they can be used to write 126 # other python scripts that would otherwise have to implement their own 127 # xml parser. See makehsl.py for an example of usage. 128 # 129 # (note: no classes are created for basic types, so no list for those) 130 xml_enum = [] 131 xml_alias = [] 132 xml_bit_struct = [] 133 xml_struct = [] 134 135 @classmethod
136 - def vercondFilter(cls, expression):
137 raise NotImplementedError
138
139 140 -class StructAttribute(object):
141 """Helper class to collect attribute data of struct add tags.""" 142 143 name = None 144 """The name of this member variable.""" 145 146 type_ = None 147 """The type of this member variable (type is ``str`` for forward 148 declarations, and resolved to :class:`BasicBase` or 149 :class:`StructBase` later). 150 """ 151 152 default = None 153 """The default value of this member variable.""" 154 155 template = None 156 """The template type of this member variable (initially ``str``, 157 resolved to :class:`BasicBase` or :class:`StructBase` at the end 158 of the xml parsing), and if there is no template type, then this 159 variable will equal ``type(None)``. 160 """ 161 162 arg = None 163 """The argument of this member variable.""" 164 165 arr1 = None 166 """The first array size of this member variable, as 167 :class:`Expression` or ``type(None)``. 168 """ 169 170 arr2 = None 171 """The second array size of this member variable, as 172 :class:`Expression` or ``type(None)``. 173 """ 174 175 cond = None 176 """The condition of this member variable, as 177 :class:`Expression` or ``type(None)``. 178 """ 179 180 ver1 = None 181 """The first version this member exists, as ``int``, and ``None`` if 182 there is no lower limit. 183 """ 184 185 ver2 = None 186 """The last version this member exists, as ``int``, and ``None`` if 187 there is no upper limit. 188 """ 189 190 userver = None 191 """The user version this member exists, as ``int``, and ``None`` if 192 it exists for all user versions. 193 """ 194 195 is_abstract = False 196 """Whether the attribute is abstract or not (read and written).""" 197
198 - def __init__(self, cls, attrs):
199 """Initialize attribute from the xml attrs dictionary of an 200 add tag. 201 202 :param cls: The class where all types reside. 203 :param attrs: The xml add tag attribute dictionary.""" 204 # mandatory parameters 205 self.displayname = attrs["name"] 206 self.name = cls.name_attribute(self.displayname) 207 try: 208 attrs_type_str = attrs["type"] 209 except KeyError: 210 raise AttributeError("'%s' is missing a type attribute" 211 % self.displayname) 212 if attrs_type_str != "TEMPLATE": 213 try: 214 self.type_ = getattr(cls, attrs_type_str) 215 except AttributeError: 216 # forward declaration, resolved at endDocument 217 self.type_ = attrs_type_str 218 else: 219 self.type_ = type(None) # type determined at runtime 220 # optional parameters 221 self.default = attrs.get("default") 222 self.template = attrs.get("template") # resolved in endDocument 223 self.arg = attrs.get("arg") 224 self.arr1 = attrs.get("arr1") 225 self.arr2 = attrs.get("arr2") 226 self.cond = attrs.get("cond") 227 self.vercond = attrs.get("vercond") 228 self.ver1 = attrs.get("ver1") 229 self.ver2 = attrs.get("ver2") 230 self.userver = attrs.get("userver") 231 self.doc = "" # handled in xml parser's characters function 232 self.is_abstract = (attrs.get("abstract") == "1") 233 234 # post-processing 235 if self.default: 236 try: 237 tmp = self.type_() 238 tmp.set_value(self.default) 239 self.default = tmp.get_value() 240 del tmp 241 except Exception: 242 # conversion failed; not a big problem 243 self.default = None 244 if self.arr1: 245 self.arr1 = Expression(self.arr1, cls.name_attribute) 246 if self.arr2: 247 self.arr2 = Expression(self.arr2, cls.name_attribute) 248 if self.cond: 249 self.cond = Expression(self.cond, cls.name_attribute) 250 if self.vercond: 251 self.vercond = Expression(self.vercond, cls.vercondFilter) 252 #print(self.vercond) 253 if self.arg: 254 try: 255 self.arg = int(self.arg) 256 except ValueError: 257 self.arg = cls.name_attribute(self.arg) 258 if self.userver: 259 self.userver = int(self.userver) 260 if self.ver1: 261 self.ver1 = cls.version_number(self.ver1) 262 if self.ver2: 263 self.ver2 = cls.version_number(self.ver2)
264
265 266 -class BitStructAttribute(object):
267 """Helper class to collect attribute data of bitstruct bits tags.""" 268
269 - def __init__(self, cls, attrs):
270 """Initialize attribute from the xml attrs dictionary of an 271 add tag. 272 273 :param cls: The class where all types reside. 274 :param attrs: The xml add tag attribute dictionary.""" 275 # mandatory parameters 276 self.name = cls.name_attribute(attrs["name"]) 277 self.numbits = int(cls.name_attribute(attrs["numbits"])) 278 # optional parameters 279 self.default = attrs.get("default") 280 self.cond = attrs.get("cond") 281 self.ver1 = attrs.get("ver1") 282 self.ver2 = attrs.get("ver2") 283 self.userver = attrs.get("userver") 284 self.doc = "" # handled in xml parser's characters function 285 286 # post-processing 287 if self.default: 288 self.default = int(self.default) 289 if self.cond: 290 self.cond = Expression(self.cond, cls.name_attribute) 291 if self.userver: 292 self.userver = int(self.userver) 293 if self.ver1: 294 self.ver1 = cls.version_number(self.ver1) 295 if self.ver2: 296 self.ver2 = cls.version_number(self.ver2)
297
298 299 -class XmlError(Exception):
300 """The XML handler will throw this exception if something goes wrong while 301 parsing.""" 302 pass
303
304 305 -class XmlSaxHandler(xml.sax.handler.ContentHandler):
306 """This class contains all functions for parsing the xml and converting 307 the xml structure into Python classes.""" 308 tag_file = 1 309 tag_version = 2 310 tag_basic = 3 311 tag_alias = 4 312 tag_enum = 5 313 tag_option = 6 314 tag_bit_struct = 7 315 tag_struct = 8 316 tag_attribute = 9 317 tag_bits = 10 318 319 tags = { 320 "fileformat": tag_file, 321 "version": tag_version, 322 "basic": tag_basic, 323 "alias": tag_alias, 324 "enum": tag_enum, 325 "option": tag_option, 326 "bitstruct": tag_bit_struct, 327 "struct": tag_struct, 328 "bits": tag_bits, 329 "add": tag_attribute} 330 331 # for compatibility with niftools 332 tags_niftools = { 333 "niftoolsxml": tag_file, 334 "compound": tag_struct, 335 "niobject": tag_struct, 336 "bitflags": tag_bit_struct} 337
338 - def __init__(self, cls, name, bases, dct):
339 """Set up the xml parser. 340 341 Upon instantiation this function does the following: 342 - Creates a dictionary C{cls.versions} which maps each supported 343 version strings onto a version integer. 344 - Creates a dictionary C{cls.games} which maps each supported game 345 onto a list of versions. 346 - Makes an alias C{self.cls} for C{cls}. 347 - Initializes a stack C{self.stack} of xml tags. 348 - Initializes the current tag. 349 """ 350 # initialize base class (no super because base class is old style) 351 xml.sax.handler.ContentHandler.__init__(self) 352 353 # save dictionary for future use 354 self.dct = dct 355 356 # initialize dictionaries 357 # cls.version maps each supported version string to a version number 358 cls.versions = {} 359 # cls.games maps each supported game to a list of header version 360 # numbers 361 cls.games = {} 362 # note: block versions are stored in the _games attribute of the 363 # struct class 364 365 # initialize tag stack 366 self.stack = [] 367 # keep track last element of self.stack 368 # storing this reduces overhead as profiling has shown 369 self.current_tag = None 370 371 # cls needs to be accessed in member functions, so make it an instance 372 # member variable 373 self.cls = cls 374 375 # elements for creating new classes 376 self.class_name = None 377 self.class_dict = None 378 self.class_bases = () 379 380 # elements for basic classes 381 self.basic_class = None 382 383 # elements for versions 384 self.version_string = None
385
386 - def pushTag(self, tag):
387 """Push tag C{tag} on the stack and make it the current tag. 388 389 :param tag: The tag to put on the stack.""" 390 self.stack.insert(0, tag) 391 self.current_tag = tag
392
393 - def popTag(self):
394 """Pop the current tag from the stack and return it. Also update 395 the current tag. 396 397 :return: The tag popped from the stack.""" 398 lasttag = self.stack.pop(0) 399 try: 400 self.current_tag = self.stack[0] 401 except IndexError: 402 self.current_tag = None 403 return lasttag
404
405 - def startElement(self, name, attrs):
406 """Called when parser starts parsing an element called C{name}. 407 408 This function sets up all variables for creating the class 409 in the C{self.endElement} function. For struct elements, it will set up 410 C{self.class_name}, C{self.class_bases}, and C{self.class_dict} which 411 will be used to create the class by invokation of C{type} in 412 C{self.endElement}. For basic, enum, and bitstruct elements, it will 413 set up C{self.basic_class} to link to the proper class implemented by 414 C{self.cls}. The code also performs sanity checks on the attributes. 415 416 For xml add tags, the function will add an entry to the 417 C{self.class_dict["_attrs"]} list. Note that this list is used by the 418 struct metaclass: the class attributes are created exactly from this 419 list. 420 421 :param name: The name of the xml element. 422 :param attrs: A dictionary of attributes of the element.""" 423 # get the tag identifier 424 try: 425 tag = self.tags[name] 426 except KeyError: 427 try: 428 tag = self.tags_niftools[name] 429 except KeyError: 430 raise XmlError("error unknown element '%s'" % name) 431 432 # Check the stack, if the stack does not exist then we must be 433 # at the root of the xml file, and the tag must be "fileformat". 434 # The fileformat tag has no further attributes of interest, 435 # so we can exit the function after pushing the tag on the stack. 436 if not self.stack: 437 if tag != self.tag_file: 438 raise XmlError("this is not a fileformat xml file") 439 self.pushTag(tag) 440 return 441 442 # Now do a number of things, depending on the tag that was last 443 # pushed on the stack; this is self.current_tag, and reflects the 444 # tag in which <name> is embedded. 445 # 446 # For each struct, alias, enum, and bitstruct tag, we shall 447 # create a class. So assign to C{self.class_name} the name of that 448 # class, C{self.class_bases} to the base of that class, and 449 # C{self.class_dict} to the class dictionary. 450 # 451 # For a basic tag, C{self.class_name} is the name of the 452 # class and C{self.basic_class} is the corresponding class in 453 # C{self.cls}. 454 # 455 # For a version tag, C{self.version_string} describes the version as a 456 # string. 457 if self.current_tag == self.tag_struct: 458 self.pushTag(tag) 459 # struct -> attribute 460 if tag == self.tag_attribute: 461 # add attribute to class dictionary 462 self.class_dict["_attrs"].append( 463 StructAttribute(self.cls, attrs)) 464 # struct -> version 465 elif tag == self.tag_version: 466 # set the version string 467 self.version_string = str(attrs["num"]) 468 self.cls.versions[self.version_string] = self.cls.version_number( 469 self.version_string) 470 # (class_dict["_games"] is updated when reading the characters) 471 else: 472 raise XmlError( 473 "only add and version tags allowed in struct declaration") 474 elif self.current_tag == self.tag_file: 475 self.pushTag(tag) 476 477 # fileformat -> struct 478 if tag == self.tag_struct: 479 self.class_name = attrs["name"] 480 # struct types can be organized in a hierarchy 481 # if inherit attribute is defined, then look for corresponding 482 # base block 483 class_basename = attrs.get("inherit") 484 if class_basename: 485 # if that base struct has not yet been assigned to a 486 # class, then we have a problem 487 try: 488 self.class_bases += ( 489 getattr(self.cls, class_basename), ) 490 except KeyError: 491 raise XmlError( 492 "typo, or forward declaration of struct %s" 493 % class_basename) 494 else: 495 self.class_bases = (StructBase,) 496 # istemplate attribute is optional 497 # if not set, then the struct is not a template 498 # set attributes (see class StructBase) 499 self.class_dict = { 500 "_is_template": attrs.get("istemplate") == "1", 501 "_attrs": [], 502 "_games": {}, 503 "__doc__": "", 504 "__module__": self.cls.__module__} 505 506 # fileformat -> basic 507 elif tag == self.tag_basic: 508 self.class_name = attrs["name"] 509 # Each basic type corresponds to a type defined in C{self.cls}. 510 # The link between basic types and C{self.cls} types is done 511 # via the name of the class. 512 self.basic_class = getattr(self.cls, self.class_name) 513 # check the class variables 514 is_template = (attrs.get("istemplate") == "1") 515 if self.basic_class._is_template != is_template: 516 raise XmlError( 517 'class %s should have _is_template = %s' 518 % (self.class_name, is_template)) 519 520 # fileformat -> enum 521 elif tag == self.tag_enum: 522 self.class_bases += (EnumBase,) 523 self.class_name = attrs["name"] 524 try: 525 numbytes = int(attrs["numbytes"]) 526 except KeyError: 527 # niftools format uses a storage 528 # get number of bytes from that 529 typename = attrs["storage"] 530 try: 531 typ = getattr(self.cls, typename) 532 except AttributeError: 533 raise XmlError( 534 "typo, or forward declaration of type %s" 535 % typename) 536 numbytes = typ.get_size() 537 self.class_dict = {"__doc__": "", 538 "_numbytes": numbytes, 539 "_enumkeys": [], "_enumvalues": [], 540 "__module__": self.cls.__module__} 541 542 # fileformat -> alias 543 elif tag == self.tag_alias: 544 self.class_name = attrs["name"] 545 typename = attrs["type"] 546 try: 547 self.class_bases += (getattr(self.cls, typename),) 548 except AttributeError: 549 raise XmlError( 550 "typo, or forward declaration of type %s" % typename) 551 self.class_dict = {"__doc__": "", 552 "__module__": self.cls.__module__} 553 554 # fileformat -> bitstruct 555 # this works like an alias for now, will add special 556 # BitStruct base class later 557 elif tag == self.tag_bit_struct: 558 self.class_bases += (BitStructBase,) 559 self.class_name = attrs["name"] 560 try: 561 numbytes = int(attrs["numbytes"]) 562 except KeyError: 563 # niftools style: storage attribute 564 numbytes = getattr(self.cls, attrs["storage"]).get_size() 565 self.class_dict = {"_attrs": [], "__doc__": "", 566 "_numbytes": numbytes, 567 "__module__": self.cls.__module__} 568 569 # fileformat -> version 570 elif tag == self.tag_version: 571 self.version_string = str(attrs["num"]) 572 self.cls.versions[self.version_string] = self.cls.version_number( 573 self.version_string) 574 # (self.cls.games is updated when reading the characters) 575 576 else: 577 raise XmlError(""" 578 expected basic, alias, enum, bitstruct, struct, or version, 579 but got %s instead""" % name) 580 581 elif self.current_tag == self.tag_version: 582 raise XmlError("version tag must not contain any sub tags") 583 584 elif self.current_tag == self.tag_alias: 585 raise XmlError("alias tag must not contain any sub tags") 586 587 elif self.current_tag == self.tag_enum: 588 self.pushTag(tag) 589 if not tag == self.tag_option: 590 raise XmlError("only option tags allowed in enum declaration") 591 value = attrs["value"] 592 try: 593 # note: use long rather than int to work around 0xffffffff 594 # error in qskope 595 value = long(value) 596 except ValueError: 597 value = long(value, 16) 598 self.class_dict["_enumkeys"].append(attrs["name"]) 599 self.class_dict["_enumvalues"].append(value) 600 601 elif self.current_tag == self.tag_bit_struct: 602 self.pushTag(tag) 603 if tag == self.tag_bits: 604 # mandatory parameters 605 self.class_dict["_attrs"].append( 606 BitStructAttribute(self.cls, attrs)) 607 elif tag == self.tag_option: 608 # niftools compatibility, we have a bitflags field 609 # so convert value into numbits 610 # first, calculate current bit position 611 bitpos = sum(bitattr.numbits 612 for bitattr in self.class_dict["_attrs"]) 613 # check if extra bits must be inserted 614 numextrabits = int(attrs["value"]) - bitpos 615 if numextrabits < 0: 616 raise XmlError("values of bitflags must be increasing") 617 if numextrabits > 0: 618 self.class_dict["_attrs"].append( 619 BitStructAttribute( 620 self.cls, 621 dict(name="Reserved Bits %i" 622 % len(self.class_dict["_attrs"]), 623 numbits=numextrabits))) 624 # add the actual attribute 625 self.class_dict["_attrs"].append( 626 BitStructAttribute( 627 self.cls, 628 dict(name=attrs["name"], numbits=1))) 629 else: 630 raise XmlError( 631 "only bits tags allowed in struct type declaration") 632 633 else: 634 raise XmlError("unhandled tag %s" % name)
635
636 - def endElement(self, name):
637 """Called at the end of each xml tag. 638 639 Creates classes.""" 640 if not self.stack: 641 raise XmlError("mismatching end element tag for element %s" % name) 642 try: 643 tag = self.tags[name] 644 except KeyError: 645 try: 646 tag = self.tags_niftools[name] 647 except KeyError: 648 raise XmlError("error unknown element %s" % name) 649 if self.popTag() != tag: 650 raise XmlError("mismatching end element tag for element %s" % name) 651 elif tag == self.tag_attribute: 652 return # improves performance 653 elif tag in (self.tag_struct, 654 self.tag_enum, 655 self.tag_alias, 656 self.tag_bit_struct): 657 # create class 658 # assign it to cls.<class_name> if it has not been implemented 659 # internally 660 cls_klass = getattr(self.cls, self.class_name, None) 661 if cls_klass and issubclass(cls_klass, BasicBase): 662 # overrides a basic type - not much to do 663 pass 664 else: 665 # check if we have a customizer class 666 if cls_klass: 667 # exists: create and add to base class of customizer 668 gen_klass = type( 669 "_" + str(self.class_name), 670 self.class_bases, self.class_dict) 671 setattr(self.cls, "_" + self.class_name, gen_klass) 672 # recreate the class, to ensure that the 673 # metaclass is called!! 674 # (otherwise, cls_klass does not have correct 675 # _attribute_list, etc.) 676 cls_klass = type( 677 cls_klass.__name__, 678 (gen_klass,) + cls_klass.__bases__, 679 dict(cls_klass.__dict__)) 680 setattr(self.cls, self.class_name, cls_klass) 681 # if the class derives from Data, then make an alias 682 if issubclass( 683 cls_klass, 684 pyffi.object_models.FileFormat.Data): 685 self.cls.Data = cls_klass 686 # for the stuff below 687 gen_class = cls_klass 688 else: 689 # does not yet exist: create it and assign to class dict 690 gen_klass = type( 691 str(self.class_name), self.class_bases, self.class_dict) 692 setattr(self.cls, self.class_name, gen_klass) 693 # append class to the appropriate list 694 if tag == self.tag_struct: 695 self.cls.xml_struct.append(gen_klass) 696 elif tag == self.tag_enum: 697 self.cls.xml_enum.append(gen_klass) 698 elif tag == self.tag_alias: 699 self.cls.xml_alias.append(gen_klass) 700 elif tag == self.tag_bit_struct: 701 self.cls.xml_bit_struct.append(gen_klass) 702 # reset variables 703 self.class_name = None 704 self.class_dict = None 705 self.class_bases = () 706 elif tag == self.tag_basic: 707 # link class cls.<class_name> to self.basic_class 708 setattr(self.cls, self.class_name, self.basic_class) 709 # reset variable 710 self.basic_class = None 711 elif tag == self.tag_version: 712 # reset variable 713 self.version_string = None
714
715 - def endDocument(self):
716 """Called when the xml is completely parsed. 717 718 Searches and adds class customized functions. 719 For version tags, adds version to version and game lists.""" 720 for obj in self.cls.__dict__.values(): 721 # skip objects that are not generated by the C{type} function 722 # or that do not derive from StructBase 723 if not (isinstance(obj, type) and issubclass(obj, StructBase)): 724 continue 725 # fix templates 726 for attr in obj._attrs: 727 templ = attr.template 728 if isinstance(templ, basestring): 729 attr.template = \ 730 getattr(self.cls, templ) if templ != "TEMPLATE" \ 731 else type(None) 732 attrtype = attr.type_ 733 if isinstance(attrtype, basestring): 734 attr.type_ = getattr(self.cls, attrtype)
735
736 - def characters(self, chars):
737 """Add the string C{chars} to the docstring. 738 For version tags, updates the game version list.""" 739 if self.current_tag in (self.tag_attribute, self.tag_bits): 740 self.class_dict["_attrs"][-1].doc += str(chars.strip()) 741 elif self.current_tag in (self.tag_struct, self.tag_enum, 742 self.tag_alias): 743 self.class_dict["__doc__"] += str(chars.strip()) 744 elif self.current_tag == self.tag_version: 745 # fileformat -> version 746 if self.stack[1] == self.tag_file: 747 gamesdict = self.cls.games 748 # struct -> version 749 elif self.stack[1] == self.tag_struct: 750 gamesdict = self.class_dict["_games"] 751 else: 752 raise XmlError("version parsing error at '%s'" % chars) 753 # update the gamesdict dictionary 754 for gamestr in (str(g.strip()) for g in chars.split(',')): 755 if gamestr in gamesdict: 756 gamesdict[gamestr].append( 757 self.cls.versions[self.version_string]) 758 else: 759 gamesdict[gamestr] = [ 760 self.cls.versions[self.version_string]]
761