Changeset 2979:b356de5b44dc in livinglogic.python.xist for src/ll/xist/scripts/xml2xsc.py

Show
Ignore:
Timestamp:
09/24/07 17:46:13 (12 years ago)
Author:
Walter Doerwald <walter@…>
Branch:
newparser
Message:

Change xml2xsc.py to use etree/lxml for parsing.

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • src/ll/xist/scripts/xml2xsc.py

    r2903 r2979  
    1212import sys, optparse 
    1313 
    14 from xml.sax import sax2exts, handler, xmlreader 
    15  
    1614from ll.xist import xsc, xnd, sims 
    1715 
    1816 
    19 class Handler(handler.ErrorHandler, handler.ContentHandler, handler.DTDHandler, handler.EntityResolver): 
    20     def __init__(self, name, url, sims): 
    21         self.name = name 
    22         self.url = url 
    23         self.sims = sims 
    24         self.xnd = None # will be set by startDocument() 
    25         self.stack = None # will be set by startDocument() 
    26         self.elements = {} # maps name to (xnd.Element, content set, attrname->xnd.Attr map) 
    27         self.entities = {} # maps name to xnd.Entity 
    28         self.procinsts = {} # maps name to xnd.ProcInst 
    29  
    30     def error(self, exception): 
    31         print "error: %r" % exception 
    32         raise exception 
    33  
    34     def fatalError(self, exception): 
    35         print "fatalError: %r" % exception 
    36         raise exception 
    37  
    38     def warning(self, exception): 
    39         print "warning: %r" % exception 
    40  
    41     def startDocument(self): 
    42         self.xnd = xnd.Namespace(self.name, url=self.url) 
    43         self.stack = [] 
    44  
    45     def endDocument(self): 
    46         # Put sims info into the element definitions 
    47         if self.sims == "none": 
    48             pass 
    49         elif self.sims == "simple": 
    50             for entry in self.elements.itervalues(): 
    51                 entry[0].modeltype = bool(entry[1]) 
    52         elif self.sims == "full": 
    53             for entry in self.elements.itervalues(): 
    54                 if not entry[1]: 
    55                     entry[0].modeltype = "sims.Empty" 
    56                 else: 
    57                     elements = [el for el in entry[1] if not el.startswith("#")] 
    58                     if not elements: 
    59                         if "#text" in entry[1]: 
    60                             entry[0].modeltype = "sims.NoElements" 
    61                         else: 
    62                             entry[0].modeltype = "sims.NoElementsOrText" 
    63                     else: 
    64                         if "#text" in entry[1]: 
    65                             entry[0].modeltype = "sims.ElementsOrText" 
    66                         else: 
    67                             entry[0].modeltype = "sims.Elements" 
    68                         entry[0].modelargs = [self.xnd.element(el) for el in elements] 
    69         else: 
    70             raise ValueError("unknown sims mode %r" % self.sims) 
    71  
    72     def startElement(self, name, attrs): 
    73         if name not in self.elements: 
    74             element = xnd.Element(name) 
    75             entry = self.elements[name] = (element, set(), {}) 
    76             self.xnd(element) 
    77         else: 
    78             entry = self.elements[name] 
    79         for attrname in attrs.keys(): 
    80             if attrname != "xmlns" and not attrname.startswith("xmlns:") and attrname not in entry[2]: 
    81                 attr = xnd.Attr(attrname, type=xsc.TextAttr) 
    82                 entry[0](attr) 
    83                 entry[2][attrname] = attr 
    84         if self.stack: 
    85             self.stack[-1][1].add(name) 
    86         self.stack.append(entry) 
    87  
    88     def endElement(self, name): 
    89         self.stack.pop(-1) 
    90  
    91     #def startPrefixMapping(self, prefix, uri): 
    92  
    93     #def endPrefixMapping(self, prefix): 
    94  
    95     #def startElementNS(self, name, qname, attrs): 
    96  
    97     #def endElementNS(self, name, qname): 
    98  
    99     def characters(self, content): 
    100         if self.stack and content: 
    101             if content.isspace(): 
    102                 self.stack[-1][1].add("#whitespace") 
    103             else: 
    104                 self.stack[-1][1].add("#text") 
    105  
    106     def entity(self, name): 
    107         self.skippedEntity(name) 
    108  
    109     def ignorableWhitespace(self, whitespace): 
    110         if self.stack: 
    111             self.stack[-1][1].add("#whitespace") 
    112  
    113     def comment(self, content): 
    114         if self.stack: 
    115             self.stack[-1][1].add("#comment") 
    116  
    117     def processingInstruction(self, target, data): 
    118         if self.stack: 
    119             self.stack[-1][1].add("#procinst") 
    120         if target not in self.procinsts: 
    121             procinst = xnd.ProcInst(target) 
    122             self.procinsts[name] = procinst 
    123             self.xnd(procinst) 
    124  
    125     def skippedEntity(self, name): 
    126         if self.stack: 
    127             self.stack[-1][1].add("#entity") 
    128         if name not in self.entities: 
    129             entity = xnd.Entity(name) 
    130             self.entities[name] = entity 
    131             self.xnd(entity) 
     17def iterpath(node): 
     18    yield [node] 
     19    if hasattr(node, "text") and node.text: 
     20        yield [node, node.text] 
     21    if hasattr(node, "getchildren") and node.text: 
     22        for child in node: 
     23            for path in iterpath(child): 
     24                yield [node] + path 
     25    if hasattr(node, "tail") and node.tail: 
     26        yield [node, node.tail] 
    13227 
    13328 
    134 def stream2xnd(stream, name="foo", url="http://www.example.com/foons", sims="simple", parser=None): 
    135     if parser is None or parser==[]: 
    136         from ll.xist import parsers 
    137         parser = parsers.SGMLOPParser() 
    138     elif isinstance(parser, list): 
    139         parser = sax2exts.make_parser(parser) 
     29def getelementname(node): 
     30    xmlns = None 
     31    name = node.tag 
     32    if name.startswith("{"): 
     33        (xmlns, sep, name) = name[1:].partition("}") 
     34    return (name, xmlns) 
     35 
     36 
     37def etree2xnd(sims, node): 
     38    x = xnd.Module() 
     39    elements = {} # maps (name, xmlns) to (xnd.Element, content set, attrname->xnd.Attr map) 
     40    procinsts = {} # maps name to xnd.ProcInst 
     41 
     42    for path in iterpath(node): 
     43        node = path[-1] 
     44        if "Element" in type(node).__name__: 
     45            (name, xmlns) = getelementname(node) 
     46            try: 
     47                entry = elements[(name, xmlns)] 
     48            except KeyError: 
     49                xndnode = xnd.Element(name, xmlns=xmlns) 
     50                entry = elements[(name, xmlns)] = (xndnode, set(), {}) 
     51                x(xndnode) 
     52            else: 
     53                xndnode = entry[0] 
     54            for attrname in node.keys(): 
     55                if not attrname.startswith("{") and attrname not in entry[2]: 
     56                    attr = xnd.Attr(attrname, type=xsc.TextAttr) 
     57                    entry[0](attr) 
     58                    entry[2][attrname] = attr 
     59        elif "ProcessingInstruction" in type(node).__name__: 
     60            name = node.target 
     61            try: 
     62                xndnode = procinsts[name] 
     63            except KeyError: 
     64                procinst = xnd.ProcInst(name) 
     65                procinsts[name] = procinst 
     66                xndnode = procinst 
     67        elif "Comment" in type(node).__name__: 
     68            xndnode = "#comment" 
     69        elif isinstance(node, basestring): 
     70            if node.isspace(): 
     71                xndnode = "#whitespace" 
     72            else: 
     73                xndnode = "#text" 
     74        if len(path) >= 2: 
     75            parententry = elements[getelementname(path[-2])] 
     76            parententry[1].add(xndnode) 
     77 
     78    # Put sims info into the element definitions 
     79    if sims == "none": 
     80        pass 
     81    elif sims == "simple": 
     82        for entry in elements.itervalues(): 
     83            entry[0].modeltype = bool(entry[1]) 
     84    elif sims == "full": 
     85        for entry in elements.itervalues(): 
     86            if not entry[1]: 
     87                entry[0].modeltype = "sims.Empty" 
     88            else: 
     89                elements = [el for el in entry[1] if isinstance(el, xnd.Element)] 
     90                if not elements: 
     91                    if "#text" in entry[1]: 
     92                        entry[0].modeltype = "sims.NoElements" 
     93                    else: 
     94                        entry[0].modeltype = "sims.NoElementsOrText" 
     95                else: 
     96                    if "#text" in entry[1]: 
     97                        entry[0].modeltype = "sims.ElementsOrText" 
     98                    else: 
     99                        entry[0].modeltype = "sims.Elements" 
     100                    entry[0].modelargs = elements 
     101    else: 
     102        raise ValueError("unknown sims mode %r" % sims) 
     103    return x 
     104 
     105 
     106def stream2xnd(stream, name="foo", url="http://www.example.com/foons", sims="simple", parser="etree"): 
     107    if parser == "etree": 
     108        from xml.etree import cElementTree 
     109        node = cElementTree.parse(stream).getroot() 
     110    elif parser == "lxml": 
     111        from lxml import etree 
     112        node = etree.parse(stream).getroot() 
     113    else: 
     114        raise ValueError("unknown parser %r" % parser) 
    140115     
    141     app = Handler(name, url, sims) 
    142  
    143     parser.setErrorHandler(app) 
    144     parser.setContentHandler(app) 
    145     parser.setDTDHandler(app) 
    146     parser.setEntityResolver(app) 
    147  
    148     #parser.setFeature(handler.feature_namespaces, 1) 
    149     parser.setFeature(handler.feature_external_ges, False) # Don't process external entities, but pass them to skippedEntity 
    150      
    151     source = xmlreader.InputSource() 
    152     source.setByteStream(stream) 
    153  
    154     parser.parse(source) 
    155      
    156     return app.xnd 
     116    return etree2xnd(sims, node) 
    157117 
    158118 
    159119def main(): 
    160120    p = optparse.OptionParser(usage="usage: %prog [options] <input.xml >output.py") 
    161     p.add_option("-n", "--name", dest="name", help="xmlname for the new namespace", default="foo") 
    162     p.add_option("-u", "--url", dest="url", help="xmlurl for the new namespace", default="http://www.example.com/foons") 
    163     p.add_option("-p", "--parser", dest="parser", help="parser module to use for XML parsing", action="append", default=[]) 
     121    p.add_option("-p", "--parser", dest="parser", help="parser module to use for XML parsing (etree or lxml)", choices=("etree", "lxml"), default="etree") 
    164122    choices = ["none", "simple", "full"] 
    165123    p.add_option("-s", "--sims", dest="sims", help="Create sims info? (%s)" % ", ".join(choices), metavar="MODE", default="simple") 
     
    169127        p.error("incorrect number of arguments") 
    170128        return 1 
    171     print stream2xnd(sys.stdin, name=options.name, url=options.url, sims=options.sims, parser=options.parser).aspy() 
     129    print stream2xnd(sys.stdin, sims=options.sims, parser=options.parser).aspy() 
    172130 
    173131