Changeset 3247:297a8b948e88 in livinglogic.python.xist

Show
Ignore:
Timestamp:
04/02/08 19:02:58 (12 years ago)
Author:
Walter Doerwald <walter@…>
Branch:
default
Tags:
rel-3-2-4
Message:

Add xmldecl and doctype arguments to ExpatParser?.init().

Add handle_xmldecl() and handle_doctype() to Builder.

Make SGMLOPParser.begin() and ExpatParser?.begin() exception save, so that when
an exception occurs the parser object is cleaned up again.

Files:
5 modified

Legend:

Unmodified
Added
Removed
  • NEWS.rst

    r3244 r3247  
    1 Changes in 3.2.4 (released ??/??/2008) 
     1Changes in 3.2.4 (released 04/02/2008) 
    22-------------------------------------- 
    33 
     
    88    style sheet. 
    99 
    10 *   :mod:`cssutils` 0.9.5b1 is required now. 
     10*   :mod:`cssutils` 0.9.5b2 is required now. 
    1111 
    1212*   :func:`ll.xist.css.iterrules` and :func:`ll.xist.css.applystylesheets` now 
     
    1818    the node repeated a number of times instead of one processing instruction 
    1919    node containing repeated content. 
     20 
     21*   The constructor for :class:`ll.xist.parsers.ExpatParser` now takes two 
     22    additional arguments: 
     23 
     24    :var:`xmldecl` 
     25        If this is true the XML declaration will appear in the resulting XIST 
     26        tree. 
     27 
     28    :var:`doctype` 
     29        If this is true the doctype declaration will appear in the resulting 
     30        XIST tree (however any interal DTD subset will be dropped). 
    2031 
    2132 
  • setup.py

    r3239 r3247  
    196196args = dict( 
    197197    name="ll-xist", 
    198     version="3.2.3", 
     198    version="3.2.4", 
    199199    description="Extensible HTML/XML generator", 
    200200    long_description=descr, 
     
    233233    ], 
    234234    install_requires=[ 
    235         "cssutils == 0.9.5b1", 
     235        "cssutils == 0.9.5b2", 
    236236    ], 
    237237    namespace_packages=["ll"], 
  • src/ll/xist/ns/xml.py

    r3210 r3247  
    4343    xmlname = "xml" 
    4444 
    45     def __init__(self, version="1.0", encoding="utf-8"): 
    46         xsc.ProcInst.__init__(self, u'version="%s" encoding="%s"' % (version, encoding)) 
     45    def __init__(self, version="1.0", encoding="utf-8", standalone=None): 
     46        v = [] 
     47        v.append(u'version="%s"' % version) # According to http://www.w3.org/TR/2000/REC-xml-20001006#NT-XMLDecl version is required 
     48        if encoding is not None: 
     49            v.append(u'encoding="%s"' % encoding) 
     50        if standalone is not None: 
     51            v.append(u'standalone="%s"' % ("yes" if standalone else "no")) 
     52        xsc.ProcInst.__init__(self, u" ".join(v)) 
    4753 
    4854 
  • src/ll/xist/parsers.py

    r3222 r3247  
    2323from ll import url, xml_codec 
    2424from ll.xist import xsc, sgmlop 
    25 from ll.xist.ns import html 
     25from ll.xist.ns import xml, html 
    2626 
    2727 
     
    3030 
    3131class Parser(object): 
     32    """ 
     33    Basic parser interface. 
     34    """ 
    3235    def __init__(self): 
    3336        self.application = None 
    3437 
    3538    def begin(self, application): 
     39        """ 
     40        Start parsing. Events will be passed to :var:`application`, which must 
     41        implement a handler for each event type. 
     42        """ 
    3643        self.application = application 
    3744 
    3845    def end(self): 
     46        """ 
     47        Finish parsing. 
     48        """ 
    3949        self.application = None 
    4050 
    4151    def feed(self, data, final): 
    42         pass 
     52        """ 
     53        Feed :var:`data` (a byte string) to the parser. If :var:`final` is true 
     54        this will be the last call to :meth:`feed`. 
     55        """ 
    4356 
    4457 
    4558class SGMLOPParser(Parser): 
     59    """ 
     60    A parser based of :mod:`sgmlop`. 
     61    """ 
    4662    def __init__(self, encoding=None): 
     63        """ 
     64        Create a new :class:`SGMLOPParser` object. 
     65        """ 
    4766        Parser.__init__(self) 
    4867        self.encoding = encoding 
     
    5271    def begin(self, application): 
    5372        Parser.begin(self, application) 
    54         self._decoder = codecs.getincrementaldecoder("xml")(encoding=self.encoding) 
    55         if self._parser is not None: 
    56             self._parser.register(None) 
    57         self._parser = sgmlop.XMLParser() 
    58         self._parser.register(self) 
     73        try: 
     74            self._decoder = codecs.getincrementaldecoder("xml")(encoding=self.encoding) 
     75            if self._parser is not None: 
     76                self._parser.register(None) 
     77            self._parser = sgmlop.XMLParser() 
     78            self._parser.register(self) 
     79        except Exception: 
     80            if self._parser is not None: 
     81                self._parser.register(None) 
     82                self._parser = None 
     83            self._decoder = None 
     84            raise 
    5985 
    6086    def feed(self, data, final): 
     
    101127 
    102128class ExpatParser(Parser): 
    103     def __init__(self, encoding=None, transcode=False): 
     129    def __init__(self, encoding=None, transcode=False, xmldecl=False, doctype=False): 
    104130        Parser.__init__(self) 
    105131        self.encoding = encoding 
     
    107133        self._decoder = None 
    108134        self._encoder = None 
     135        self._xmldecl = xmldecl 
     136        self._doctype = doctype 
     137        self._indoctype = False 
    109138        self._transcode = transcode 
    110139 
    111140    def begin(self, application): 
    112141        Parser.begin(self, application) 
    113         self._parser = expat.ParserCreate(self.encoding) 
    114         self._parser.buffer_text = True 
    115         self._parser.ordered_attributes = True 
    116         self._parser.UseForeignDTD(True) 
    117         self._parser.CharacterDataHandler = self.handle_data 
    118         self._parser.StartElementHandler = self.handle_startelement 
    119         self._parser.EndElementHandler = self.handle_endelement 
    120         self._parser.ProcessingInstructionHandler = self.handle_proc 
    121         self._parser.CommentHandler = self.handle_comment 
    122         self._parser.DefaultHandler = self.handle_default 
    123         if self._transcode: 
    124             self._decoder = codecs.getincrementaldecoder("xml")() 
    125             self._encoder = codecs.getincrementalencoder("xml")(encoding="utf-8") 
     142        try: 
     143            self._parser = expat.ParserCreate(self.encoding) 
     144            self._parser.buffer_text = True 
     145            self._parser.ordered_attributes = True 
     146            self._parser.UseForeignDTD(True) 
     147            self._parser.CharacterDataHandler = self.handle_data 
     148            self._parser.StartElementHandler = self.handle_startelement 
     149            self._parser.EndElementHandler = self.handle_endelement 
     150            self._parser.ProcessingInstructionHandler = self.handle_proc 
     151            self._parser.CommentHandler = self.handle_comment 
     152            self._parser.DefaultHandler = self.handle_default 
     153 
     154            if self._xmldecl: 
     155                self._parser.XmlDeclHandler = self.handle_xmldecl 
     156 
     157            # Always required, as we want to recognize whether a comment or PI is in the internal DTD subset 
     158            self._parser.StartDoctypeDeclHandler = self.handle_begindoctype 
     159            self._parser.EndDoctypeDeclHandler = self.handle_enddoctype 
     160 
     161            if self._transcode: 
     162                self._decoder = codecs.getincrementaldecoder("xml")() 
     163                self._encoder = codecs.getincrementalencoder("xml")(encoding="utf-8") 
     164        except Exception: 
     165            self._parser = None 
     166            self._encoder = None 
     167            self._decoder = None 
     168            raise 
    126169 
    127170    def end(self): 
     
    131174        self._decoder = None 
    132175 
     176    def handle_xmldecl(self, version, encoding, standalone): 
     177        standalone = (bool(standalone) if standalone != -1 else None) 
     178        self.application.handle_xmldecl(version, encoding, standalone, self._parser.CurrentLineNumber-1, self._parser.CurrentColumnNumber) 
     179 
     180    def handle_begindoctype(self, doctypename, systemid, publicid, has_internal_subset): 
     181        if publicid: 
     182            content = u'%s PUBLIC "%s" "%s"' % (doctypename, publicid, systemid) 
     183        elif systemid: 
     184            content = u'%s SYSTEM "%s"' % (doctypename, systemid) 
     185        else: 
     186            content = doctypename 
     187        self._indoctype = True 
     188        if self._doctype: 
     189            self.application.handle_doctype(content, self._parser.CurrentLineNumber-1, self._parser.CurrentColumnNumber) 
     190 
     191    def handle_enddoctype(self): 
     192        self._indoctype = False 
     193 
    133194    def handle_default(self, data): 
    134195        if data.startswith("&") and data.endswith(";"): 
     
    136197 
    137198    def handle_comment(self, data): 
    138         self.application.handle_comment(data, self._parser.CurrentLineNumber-1, self._parser.CurrentColumnNumber) 
     199        if not self._indoctype: 
     200            self.application.handle_comment(data, self._parser.CurrentLineNumber-1, self._parser.CurrentColumnNumber) 
    139201 
    140202    def handle_data(self, data): 
     
    154216 
    155217    def handle_proc(self, target, data): 
    156         self.application.handle_proc(target, data, self._parser.CurrentLineNumber-1, self._parser.CurrentColumnNumber) 
     218        if not self._indoctype: 
     219            self.application.handle_proc(target, data, self._parser.CurrentLineNumber-1, self._parser.CurrentColumnNumber) 
    157220 
    158221    def feed(self, data, final): 
     
    437500        """ 
    438501        def toxsc(node): 
    439             if "Element" in type(node).__name__: 
     502            name = type(node).__name__ 
     503            if "Element" in name: 
    440504                xmlns = None 
    441505                name = node.tag 
     
    461525                newnode = newnode.parsed(self, start=False) 
    462526                return newnode 
    463             elif "ProcessingInstruction" in type(node).__name__: 
     527            elif "ProcessingInstruction" in name: 
    464528                newnode = self.pool.procinst_xml(node.target, node.text) 
    465529                newnode = newnode.parsed(self) 
    466530                return newnode 
    467             elif "Comment" in type(node).__name__: 
     531            elif "Comment" in name: 
    468532                newnode = self.pool.comment(node.text) 
    469533                newnode = newnode.parsed(self) 
     
    479543 
    480544        return toxsc(tree) 
     545 
     546    def handle_xmldecl(self, version, encoding, standalone, line, col): 
     547        node = xml.XML(version=version, encoding=encoding, standalone=standalone) 
     548        self.__appendNode(node, line, col) 
     549 
     550    def handle_doctype(self, content, line, col): 
     551        node = xsc.DocType(content) 
     552        self.__appendNode(node, line, col) 
    481553 
    482554    def handle_enterstarttag(self, name, line, col): 
  • test/test_xist_parse.py

    r3189 r3247  
    2323from ll import url 
    2424from ll.xist import xsc, parsers 
    25 from ll.xist.ns import chars, html, ihtml, specials, ruby 
     25from ll.xist.ns import xml, chars, html, ihtml, specials, ruby 
    2626 
    2727 
     
    271271    py.test.raises(xsc.IllegalElementError, parsers.parsestring, s, pool=xsc.Pool()) 
    272272 
     273 
    273274def test_parseemptyattribute(): 
    274275    e = parsers.parsestring("<a target=''/>", pool=xsc.Pool(html)) 
    275276    assert "target" in e[0].attrs 
     277 
     278 
     279def test_expat_xmldecl(): 
     280    e = parsers.parsestring("<?xml version='1.0' encoding='utf-8' standalone='yes'?><a/>", parser=parsers.ExpatParser()) 
     281    assert not isinstance(e[0], xml.XML) 
     282 
     283    e = parsers.parsestring("<a/>", parser=parsers.ExpatParser(xmldecl=True)) 
     284    assert not isinstance(e[0], xml.XML) 
     285 
     286    e = parsers.parsestring("<?xml version='1.0'?><a/>", parser=parsers.ExpatParser(xmldecl=True)) 
     287    assert isinstance(e[0], xml.XML) 
     288    assert e[0].content == u'version="1.0"' 
     289 
     290    e = parsers.parsestring("<?xml version='1.0' encoding='utf-8'?><a/>", parser=parsers.ExpatParser(xmldecl=True)) 
     291    assert isinstance(e[0], xml.XML) 
     292    assert e[0].content == u'version="1.0" encoding="utf-8"' 
     293 
     294    e = parsers.parsestring("<?xml version='1.0' encoding='utf-8' standalone='yes'?><a/>", parser=parsers.ExpatParser(xmldecl=True)) 
     295    assert isinstance(e[0], xml.XML) 
     296    assert e[0].content == u'version="1.0" encoding="utf-8" standalone="yes"' 
     297 
     298 
     299def test_expat_doctype(): 
     300    e = parsers.parsestring('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><a/>', parser=parsers.ExpatParser()) 
     301    assert not isinstance(e[0], xsc.DocType) 
     302 
     303    e = parsers.parsestring('<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><a/>', parser=parsers.ExpatParser(doctype=True)) 
     304    assert isinstance(e[0], xsc.DocType) 
     305    assert e[0].content == html.DocTypeXHTML11().content 
     306 
     307    e = parsers.parsestring('<!DOCTYPE html><a/>', parser=parsers.ExpatParser(doctype=True)) 
     308    assert isinstance(e[0], xsc.DocType) 
     309    assert e[0].content == "html" 
     310 
     311    e = parsers.parsestring('<!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"><a/>', parser=parsers.ExpatParser(doctype=True)) 
     312    assert isinstance(e[0], xsc.DocType) 
     313    assert e[0].content == u'html SYSTEM "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"' 
     314 
     315    e = parsers.parsestring('<!DOCTYPE a [<!ELEMENT a EMPTY><!--gurk-->]><a/>', parser=parsers.ExpatParser(doctype=True)) 
     316    assert isinstance(e[0], xsc.DocType) 
     317    assert e[0].content == u'a' # Internal subset gets dropped