diff -r ae8b054155c1 Lib/test/test_xml_etree.py --- a/Lib/test/test_xml_etree.py Sat Jul 06 10:25:04 2013 +0200 +++ b/Lib/test/test_xml_etree.py Sun Jul 07 16:19:16 2013 +0200 @@ -79,6 +79,20 @@ """ +SAMPLE_PRETTYPRINTED_XML = """\ + + text + tail + + + prefixtext + text + tail + + +\ +""" + ENTITY_XML = """\ @@ -668,6 +682,18 @@ elem = ET.fromstring("text") self.assertEqual(ET.tostring(elem), b'text') + def _munge_whitespace(self, elem): + elem.text = ' %s ' % elem.text.strip() if elem.text else ' ' + elem.tail = ' %s ' % elem.tail.strip() if elem.tail else ' ' + for e in elem: + self._munge_whitespace(e) + + def test_prettyprint(self): + elem = ET.fromstring(SAMPLE_PRETTYPRINTED_XML) + self._munge_whitespace(elem) + prettyprinted = ET.tostring(elem, pretty=True) + self.assertEqual(prettyprinted, bytes(SAMPLE_PRETTYPRINTED_XML, 'utf8')) + def test_encoding(self): def check(encoding, body=''): xml = ("%s" % diff -r ae8b054155c1 Lib/xml/etree/ElementTree.py --- a/Lib/xml/etree/ElementTree.py Sat Jul 06 10:25:04 2013 +0200 +++ b/Lib/xml/etree/ElementTree.py Sun Jul 07 16:19:16 2013 +0200 @@ -723,7 +723,8 @@ xml_declaration=None, default_namespace=None, method=None, *, - short_empty_elements=True): + short_empty_elements=True, + pretty=False): """Write element tree to a file as XML. Arguments: @@ -746,6 +747,9 @@ tag, otherwise they are emitted as a pair of start/end tags + *pretty* -- enables human-readable indentation where relevant. + Off by default. + """ if not method: method = "xml" @@ -775,7 +779,8 @@ qnames, namespaces = _namespaces(self._root, default_namespace) serialize = _serialize[method] serialize(write, self._root, qnames, namespaces, - short_empty_elements=short_empty_elements) + short_empty_elements=short_empty_elements, + pretty=pretty, depth=0) def write_c14n(self, file): # lxml.etree compatibility. use output method instead @@ -898,7 +903,10 @@ return qnames, namespaces def _serialize_xml(write, elem, qnames, namespaces, - short_empty_elements, **kwargs): + short_empty_elements, pretty, depth, + tail_depth=None, **kwargs): + if tail_depth is None: + tail_depth = depth and (depth - 1) tag = elem.tag text = elem.text if tag is Comment: @@ -906,13 +914,24 @@ elif tag is ProcessingInstruction: write("" % text) else: + if pretty: + text = text and text.strip() + if len(elem): + indent = '\n' + ' ' * (depth + 1) + if text: + text = indent + text + indent + else: + text = indent tag = qnames[tag] if tag is None: if text: write(_escape_cdata(text)) - for e in elem: + for n, e in enumerate(elem): + new_tail_depth = depth if n + 1 == len(elem) else depth + 1 _serialize_xml(write, e, qnames, None, - short_empty_elements=short_empty_elements) + short_empty_elements=short_empty_elements, + pretty=pretty, depth=depth + 1, + tail_depth=new_tail_depth) else: write("<" + tag) items = list(elem.items()) @@ -938,14 +957,25 @@ write(">") if text: write(_escape_cdata(text)) - for e in elem: + for n, e in enumerate(elem): + new_tail_depth = depth if n + 1 == len(elem) else depth + 1 _serialize_xml(write, e, qnames, None, - short_empty_elements=short_empty_elements) + short_empty_elements=short_empty_elements, + pretty=pretty, depth=depth + 1, + tail_depth=new_tail_depth) write("") else: write(" />") - if elem.tail: - write(_escape_cdata(elem.tail)) + tail = elem.tail + if pretty: + tail = tail and tail.strip() + tail_indent = '\n' + ' ' * tail_depth if depth else '' + if tail: + tail = '\n' + ' ' * depth + tail + tail_indent + else: + tail = tail_indent + if tail: + write(_escape_cdata(tail)) HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr", "img", "input", "isindex", "link", "meta", "param") @@ -1108,7 +1138,8 @@ # -------------------------------------------------------------------- def tostring(element, encoding=None, method=None, *, - short_empty_elements=True): + short_empty_elements=True, + pretty=False): """Generate string representation of XML element. All subelements are included. If encoding is "unicode", a string @@ -1123,7 +1154,8 @@ """ stream = io.StringIO() if encoding == 'unicode' else io.BytesIO() ElementTree(element).write(stream, encoding, method=method, - short_empty_elements=short_empty_elements) + short_empty_elements=short_empty_elements, + pretty=pretty) return stream.getvalue() class _ListDataStream(io.BufferedIOBase): @@ -1144,11 +1176,13 @@ return len(self.lst) def tostringlist(element, encoding=None, method=None, *, - short_empty_elements=True): + short_empty_elements=True, + pretty=False): lst = [] stream = _ListDataStream(lst) ElementTree(element).write(stream, encoding, method=method, - short_empty_elements=short_empty_elements) + short_empty_elements=short_empty_elements, + pretty=pretty) return lst