# HG changeset patch # User cmlenz # Date 1177530132 0 # Node ID af0122a5aa4b1d62c176fb40263f5a30ab749784 # Parent 9101eb8a68b6d1f0ac6de0f423d609218f3c5362 Port [558] to 0.4.x. diff --git a/ChangeLog b/ChangeLog --- a/ChangeLog +++ b/ChangeLog @@ -4,6 +4,8 @@ * Fix incorrect reference to translation function in the I18N filter. * The `ET()` function now correctly handles attributes with a namespace. + * XML declarations are now processed internally, as well as written to the + output when XML serialization is used (ticket #111). Version 0.4 diff --git a/genshi/core.py b/genshi/core.py --- a/genshi/core.py +++ b/genshi/core.py @@ -56,6 +56,7 @@ START = StreamEventKind('START') #: a start tag END = StreamEventKind('END') #: an end tag TEXT = StreamEventKind('TEXT') #: literal text + XML_DECL = StreamEventKind('XML_DECL') #: XML declaration DOCTYPE = StreamEventKind('DOCTYPE') #: doctype declaration START_NS = StreamEventKind('START_NS') #: start namespace mapping END_NS = StreamEventKind('END_NS') #: end namespace mapping @@ -208,6 +209,7 @@ START = Stream.START END = Stream.END TEXT = Stream.TEXT +XML_DECL = Stream.XML_DECL DOCTYPE = Stream.DOCTYPE START_NS = Stream.START_NS END_NS = Stream.END_NS diff --git a/genshi/input.py b/genshi/input.py --- a/genshi/input.py +++ b/genshi/input.py @@ -26,7 +26,7 @@ from StringIO import StringIO from genshi.core import Attrs, QName, Stream, stripentities -from genshi.core import DOCTYPE, START, END, START_NS, END_NS, TEXT, \ +from genshi.core import START, END, XML_DECL, DOCTYPE, TEXT, START_NS, END_NS, \ START_CDATA, END_CDATA, PI, COMMENT __all__ = ['ET', 'ParseError', 'XMLParser', 'XML', 'HTMLParser', 'HTML'] @@ -123,6 +123,7 @@ parser.StartCdataSectionHandler = self._handle_start_cdata parser.EndCdataSectionHandler = self._handle_end_cdata parser.ProcessingInstructionHandler = self._handle_pi + parser.XmlDeclHandler = self._handle_xml_decl parser.CommentHandler = self._handle_comment # Tell Expat that we'll handle non-XML entities ourselves @@ -216,6 +217,9 @@ def _handle_data(self, text): self._enqueue(TEXT, text) + def _handle_xml_decl(self, version, encoding, standalone): + self._enqueue(XML_DECL, (version, encoding, standalone)) + def _handle_doctype(self, name, sysid, pubid, has_internal_subset): self._enqueue(DOCTYPE, (name, pubid, sysid)) diff --git a/genshi/output.py b/genshi/output.py --- a/genshi/output.py +++ b/genshi/output.py @@ -23,7 +23,7 @@ import re from genshi.core import escape, Attrs, Markup, Namespace, QName, StreamEventKind -from genshi.core import DOCTYPE, START, END, START_NS, END_NS, TEXT, \ +from genshi.core import START, END, TEXT, XML_DECL, DOCTYPE, START_NS, END_NS, \ START_CDATA, END_CDATA, PI, COMMENT, XML_NAMESPACE __all__ = ['DocType', 'XMLSerializer', 'XHTMLSerializer', 'HTMLSerializer', @@ -87,7 +87,7 @@ self.filters.append(NamespaceFlattener(prefixes=namespace_prefixes)) def __call__(self, stream): - have_doctype = False + have_decl = have_doctype = False in_cdata = False stream = chain(self.preamble, stream) @@ -115,6 +115,18 @@ elif kind is COMMENT: yield Markup('' % data) + elif kind is XML_DECL and not have_decl: + version, encoding, standalone = data + buf = ['\n') + yield Markup(u''.join(buf)) + have_decl = True + elif kind is DOCTYPE and not have_doctype: name, pubid, sysid = data buf = [' \xf6 """.encode('iso-8859-1'), encoding='iso-8859-1') - self.assertEqual(u"""
+ self.assertEqual(u"""\n
\xf6
""", unicode(tmpl.generate())) @@ -228,7 +228,7 @@ Size """) - self.assertEqual(""" + self.assertEqual("""\n """, str(tmpl.generate())) diff --git a/genshi/tests/input.py b/genshi/tests/input.py --- a/genshi/tests/input.py +++ b/genshi/tests/input.py @@ -71,7 +71,7 @@
\xf6
""".encode('iso-8859-1') events = list(XMLParser(StringIO(text))) - kind, data, pos = events[1] + kind, data, pos = events[2] self.assertEqual(Stream.TEXT, kind) self.assertEqual(u'\xf6', data) @@ -181,6 +181,33 @@ self.assertEqual(u'php', target) self.assertEqual(u'echo "Foobar"', data) + def test_xmldecl(self): + text = '' + events = list(XMLParser(StringIO(text))) + kind, (version, encoding, standalone), pos = events[0] + self.assertEqual(Stream.XML_DECL, kind) + self.assertEqual(u'1.0', version) + self.assertEqual(None, encoding) + self.assertEqual(-1, standalone) + + def test_xmldecl_encoding(self): + text = '' + events = list(XMLParser(StringIO(text))) + kind, (version, encoding, standalone), pos = events[0] + self.assertEqual(Stream.XML_DECL, kind) + self.assertEqual(u'1.0', version) + self.assertEqual(u'utf-8', encoding) + self.assertEqual(-1, standalone) + + def test_xmldecl_standalone(self): + text = '' + events = list(XMLParser(StringIO(text))) + kind, (version, encoding, standalone), pos = events[0] + self.assertEqual(Stream.XML_DECL, kind) + self.assertEqual(u'1.0', version) + self.assertEqual(None, encoding) + self.assertEqual(1, standalone) + def test_processing_instruction_trailing_qmark(self): text = '' events = list(HTMLParser(StringIO(text)))