>>> ET.register_namespace('test10777', 'http://myuri/')
"""
-def check_html_empty_elems_serialization(self):
- # issue 15970
- # from http://www.w3.org/TR/html401/index/elements.html
- """
+# --------------------------------------------------------------------
- >>> empty_elems = ['AREA', 'BASE', 'BASEFONT', 'BR', 'COL', 'FRAME', 'HR',
- ... 'IMG', 'INPUT', 'ISINDEX', 'LINK', 'META', 'PARAM']
- >>> elems = ''.join('<%s />' % elem for elem in empty_elems)
- >>> serialize(ET.XML('<html>%s</html>' % elems), method='html')
- '<html><AREA><BASE><BASEFONT><BR><COL><FRAME><HR><IMG><INPUT><ISINDEX><LINK><META><PARAM></html>'
- >>> serialize(ET.XML('<html>%s</html>' % elems.lower()), method='html')
- '<html><area><base><basefont><br><col><frame><hr><img><input><isindex><link><meta><param></html>'
- >>> elems = ''.join('<%s></%s>' % (elem, elem) for elem in empty_elems)
- >>> serialize(ET.XML('<html>%s</html>' % elems), method='html')
- '<html><AREA><BASE><BASEFONT><BR><COL><FRAME><HR><IMG><INPUT><ISINDEX><LINK><META><PARAM></html>'
- >>> serialize(ET.XML('<html>%s</html>' % elems.lower()), method='html')
- '<html><area><base><basefont><br><col><frame><hr><img><input><isindex><link><meta><param></html>'
- """
+class BasicElementTest(unittest.TestCase):
+ def test_augmentation_type_errors(self):
+ e = ET.Element('joe')
+ self.assertRaises(TypeError, e.append, 'b')
+ self.assertRaises(TypeError, e.extend, [ET.Element('bar'), 'foo'])
+ self.assertRaises(TypeError, e.insert, 0, 'foo')
+
+ def test_cyclic_gc(self):
+ class Dummy:
+ pass
+
+ # Test the shortest cycle: d->element->d
+ d = Dummy()
+ d.dummyref = ET.Element('joe', attr=d)
+ wref = weakref.ref(d)
+ del d
+ gc_collect()
+ self.assertIsNone(wref())
+
+ # A longer cycle: d->e->e2->d
+ e = ET.Element('joe')
+ d = Dummy()
+ d.dummyref = e
+ wref = weakref.ref(d)
+ e2 = ET.SubElement(e, 'foo', attr=d)
+ del d, e, e2
+ gc_collect()
+ self.assertIsNone(wref())
+
+ # A cycle between Element objects as children of one another
+ # e1->e2->e3->e1
+ e1 = ET.Element('e1')
+ e2 = ET.Element('e2')
+ e3 = ET.Element('e3')
+ e1.append(e2)
+ e2.append(e2)
+ e3.append(e1)
+ wref = weakref.ref(e1)
+ del e1, e2, e3
+ gc_collect()
+ self.assertIsNone(wref())
+
+ def test_weakref(self):
+ flag = False
+ def wref_cb(w):
+ nonlocal flag
+ flag = True
+ e = ET.Element('e')
+ wref = weakref.ref(e, wref_cb)
+ self.assertEqual(wref().tag, 'e')
+ del e
+ self.assertEqual(flag, True)
+ self.assertEqual(wref(), None)
+
+
+class ElementTreeTest(unittest.TestCase):
+ def test_istype(self):
+ self.assertIsInstance(ET.ParseError, type)
+ self.assertIsInstance(ET.QName, type)
+ self.assertIsInstance(ET.ElementTree, type)
+ self.assertIsInstance(ET.Element, type)
+ self.assertIsInstance(ET.TreeBuilder, type)
+ self.assertIsInstance(ET.XMLParser, type)
+
+ def test_Element_subclass_trivial(self):
+ class MyElement(ET.Element):
+ pass
+
+ mye = MyElement('foo')
+ self.assertIsInstance(mye, ET.Element)
+ self.assertIsInstance(mye, MyElement)
+ self.assertEqual(mye.tag, 'foo')
+
+ # test that attribute assignment works (issue 14849)
+ mye.text = "joe"
+ self.assertEqual(mye.text, "joe")
+
+ def test_Element_subclass_constructor(self):
+ class MyElement(ET.Element):
+ def __init__(self, tag, attrib={}, **extra):
+ super(MyElement, self).__init__(tag + '__', attrib, **extra)
+
+ mye = MyElement('foo', {'a': 1, 'b': 2}, c=3, d=4)
+ self.assertEqual(mye.tag, 'foo__')
+ self.assertEqual(sorted(mye.items()),
+ [('a', 1), ('b', 2), ('c', 3), ('d', 4)])
+
+ def test_Element_subclass_new_method(self):
+ class MyElement(ET.Element):
+ def newmethod(self):
+ return self.tag
+
+ mye = MyElement('joe')
+ self.assertEqual(mye.newmethod(), 'joe')
+
++ def test_html_empty_elems_serialization(self):
++ # issue 15970
++ # from http://www.w3.org/TR/html401/index/elements.html
++ for element in ['AREA', 'BASE', 'BASEFONT', 'BR', 'COL', 'FRAME', 'HR',
++ 'IMG', 'INPUT', 'ISINDEX', 'LINK', 'META', 'PARAM']:
++ for elem in [element, element.lower()]:
++ expected = '<%s>' % elem
++ serialized = serialize(ET.XML('<%s />' % elem), method='html')
++ self.assertEqual(serialized, expected)
++ serialized = serialize(ET.XML('<%s></%s>' % (elem,elem)),
++ method='html')
++ self.assertEqual(serialized, expected)
+
+class ElementIterTest(unittest.TestCase):
+ def _ilist(self, elem, tag=None):
+ return summarize_list(elem.iter(tag))
+
+ def test_basic(self):
+ doc = ET.XML("<html><body>this is a <i>paragraph</i>.</body>..</html>")
+ self.assertEqual(self._ilist(doc), ['html', 'body', 'i'])
+ self.assertEqual(self._ilist(doc.find('body')), ['body', 'i'])
+ self.assertEqual(next(doc.iter()).tag, 'html')
+ self.assertEqual(''.join(doc.itertext()), 'this is a paragraph...')
+ self.assertEqual(''.join(doc.find('body').itertext()),
+ 'this is a paragraph.')
+ self.assertEqual(next(doc.itertext()), 'this is a ')
+
+ # iterparse should return an iterator
+ sourcefile = serialize(doc, to_string=False)
+ self.assertEqual(next(ET.iterparse(sourcefile))[0], 'end')
+
+ tree = ET.ElementTree(None)
+ self.assertRaises(AttributeError, tree.iter)
+
+ def test_corners(self):
+ # single root, no subelements
+ a = ET.Element('a')
+ self.assertEqual(self._ilist(a), ['a'])
+
+ # one child
+ b = ET.SubElement(a, 'b')
+ self.assertEqual(self._ilist(a), ['a', 'b'])
+
+ # one child and one grandchild
+ c = ET.SubElement(b, 'c')
+ self.assertEqual(self._ilist(a), ['a', 'b', 'c'])
+
+ # two children, only first with grandchild
+ d = ET.SubElement(a, 'd')
+ self.assertEqual(self._ilist(a), ['a', 'b', 'c', 'd'])
+
+ # replace first child by second
+ a[0] = a[1]
+ del a[1]
+ self.assertEqual(self._ilist(a), ['a', 'd'])
+
+ def test_iter_by_tag(self):
+ doc = ET.XML('''
+ <document>
+ <house>
+ <room>bedroom1</room>
+ <room>bedroom2</room>
+ </house>
+ <shed>nothing here
+ </shed>
+ <house>
+ <room>bedroom8</room>
+ </house>
+ </document>''')
+
+ self.assertEqual(self._ilist(doc, 'room'), ['room'] * 3)
+ self.assertEqual(self._ilist(doc, 'house'), ['house'] * 2)
+
+ # make sure both tag=None and tag='*' return all tags
+ all_tags = ['document', 'house', 'room', 'room',
+ 'shed', 'house', 'room']
+ self.assertEqual(self._ilist(doc), all_tags)
+ self.assertEqual(self._ilist(doc, '*'), all_tags)
+
+
+class TreeBuilderTest(unittest.TestCase):
+ sample1 = ('<!DOCTYPE html PUBLIC'
+ ' "-//W3C//DTD XHTML 1.0 Transitional//EN"'
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
+ '<html>text</html>')
+
+ sample2 = '''<toplevel>sometext</toplevel>'''
+
+ def test_dummy_builder(self):
+ class BaseDummyBuilder:
+ def close(self):
+ return 42
+
+ class DummyBuilder(BaseDummyBuilder):
+ data = start = end = lambda *a: None
+
+ parser = ET.XMLParser(target=DummyBuilder())
+ parser.feed(self.sample1)
+ self.assertEqual(parser.close(), 42)
+
+ parser = ET.XMLParser(target=BaseDummyBuilder())
+ parser.feed(self.sample1)
+ self.assertEqual(parser.close(), 42)
+
+ parser = ET.XMLParser(target=object())
+ parser.feed(self.sample1)
+ self.assertIsNone(parser.close())
+
+ def test_subclass(self):
+ class MyTreeBuilder(ET.TreeBuilder):
+ def foobar(self, x):
+ return x * 2
+
+ tb = MyTreeBuilder()
+ self.assertEqual(tb.foobar(10), 20)
+
+ parser = ET.XMLParser(target=tb)
+ parser.feed(self.sample1)
+
+ e = parser.close()
+ self.assertEqual(e.tag, 'html')
+
+ def test_element_factory(self):
+ lst = []
+ def myfactory(tag, attrib):
+ nonlocal lst
+ lst.append(tag)
+ return ET.Element(tag, attrib)
+
+ tb = ET.TreeBuilder(element_factory=myfactory)
+ parser = ET.XMLParser(target=tb)
+ parser.feed(self.sample2)
+ parser.close()
+
+ self.assertEqual(lst, ['toplevel'])
+
+ def test_doctype(self):
+ class DoctypeParser:
+ _doctype = None
+
+ def doctype(self, name, pubid, system):
+ self._doctype = (name, pubid, system)
+
+ def close(self):
+ return self._doctype
+
+ parser = ET.XMLParser(target=DoctypeParser())
+ parser.feed(self.sample1)
+
+ self.assertEqual(parser.close(),
+ ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
+
+
+class XincludeTest(unittest.TestCase):
+ def _my_loader(self, href, parse):
+ # Used to avoid a test-dependency problem where the default loader
+ # of ElementInclude uses the pyET parser for cET tests.
+ if parse == 'xml':
+ with open(href, 'rb') as f:
+ return ET.parse(f).getroot()
+ else:
+ return None
+
+ def test_xinclude_default(self):
+ from xml.etree import ElementInclude
+ doc = xinclude_loader('default.xml')
+ ElementInclude.include(doc, self._my_loader)
+ s = serialize(doc)
+ self.assertEqual(s.strip(), '''<document>
+ <p>Example.</p>
+ <root>
+ <element key="value">text</element>
+ <element>text</element>tail
+ <empty-element />
+</root>
+</document>''')
+
+
+class XMLParserTest(unittest.TestCase):
+ sample1 = '<file><line>22</line></file>'
+ sample2 = ('<!DOCTYPE html PUBLIC'
+ ' "-//W3C//DTD XHTML 1.0 Transitional//EN"'
+ ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
+ '<html>text</html>')
+
+ def _check_sample_element(self, e):
+ self.assertEqual(e.tag, 'file')
+ self.assertEqual(e[0].tag, 'line')
+ self.assertEqual(e[0].text, '22')
+
+ def test_constructor_args(self):
+ # Positional args. The first (html) is not supported, but should be
+ # nevertheless correctly accepted.
+ parser = ET.XMLParser(None, ET.TreeBuilder(), 'utf-8')
+ parser.feed(self.sample1)
+ self._check_sample_element(parser.close())
+
+ # Now as keyword args.
+ parser2 = ET.XMLParser(encoding='utf-8', html=[{}], target=ET.TreeBuilder())
+ parser2.feed(self.sample1)
+ self._check_sample_element(parser2.close())
+
+ def test_subclass(self):
+ class MyParser(ET.XMLParser):
+ pass
+ parser = MyParser()
+ parser.feed(self.sample1)
+ self._check_sample_element(parser.close())
+
+ def test_subclass_doctype(self):
+ _doctype = None
+ class MyParserWithDoctype(ET.XMLParser):
+ def doctype(self, name, pubid, system):
+ nonlocal _doctype
+ _doctype = (name, pubid, system)
+
+ parser = MyParserWithDoctype()
+ parser.feed(self.sample2)
+ parser.close()
+ self.assertEqual(_doctype,
+ ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
+ 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
+
+
+class NamespaceParseTest(unittest.TestCase):
+ def test_find_with_namespace(self):
+ nsmap = {'h': 'hello', 'f': 'foo'}
+ doc = ET.fromstring(SAMPLE_XML_NS_ELEMS)
+
+ self.assertEqual(len(doc.findall('{hello}table', nsmap)), 1)
+ self.assertEqual(len(doc.findall('.//{hello}td', nsmap)), 2)
+ self.assertEqual(len(doc.findall('.//{foo}name', nsmap)), 1)
+
+
+class ElementSlicingTest(unittest.TestCase):
+ def _elem_tags(self, elemlist):
+ return [e.tag for e in elemlist]
+
+ def _subelem_tags(self, elem):
+ return self._elem_tags(list(elem))
+
+ def _make_elem_with_children(self, numchildren):
+ """Create an Element with a tag 'a', with the given amount of children
+ named 'a0', 'a1' ... and so on.
+
+ """
+ e = ET.Element('a')
+ for i in range(numchildren):
+ ET.SubElement(e, 'a%s' % i)
+ return e
+
+ def test_getslice_single_index(self):
+ e = self._make_elem_with_children(10)
+
+ self.assertEqual(e[1].tag, 'a1')
+ self.assertEqual(e[-2].tag, 'a8')
+
+ self.assertRaises(IndexError, lambda: e[12])
+
+ def test_getslice_range(self):
+ e = self._make_elem_with_children(6)
+
+ self.assertEqual(self._elem_tags(e[3:]), ['a3', 'a4', 'a5'])
+ self.assertEqual(self._elem_tags(e[3:6]), ['a3', 'a4', 'a5'])
+ self.assertEqual(self._elem_tags(e[3:16]), ['a3', 'a4', 'a5'])
+ self.assertEqual(self._elem_tags(e[3:5]), ['a3', 'a4'])
+ self.assertEqual(self._elem_tags(e[3:-1]), ['a3', 'a4'])
+ self.assertEqual(self._elem_tags(e[:2]), ['a0', 'a1'])
+
+ def test_getslice_steps(self):
+ e = self._make_elem_with_children(10)
+
+ self.assertEqual(self._elem_tags(e[8:10:1]), ['a8', 'a9'])
+ self.assertEqual(self._elem_tags(e[::3]), ['a0', 'a3', 'a6', 'a9'])
+ self.assertEqual(self._elem_tags(e[::8]), ['a0', 'a8'])
+ self.assertEqual(self._elem_tags(e[1::8]), ['a1', 'a9'])
+
+ def test_getslice_negative_steps(self):
+ e = self._make_elem_with_children(4)
+
+ self.assertEqual(self._elem_tags(e[::-1]), ['a3', 'a2', 'a1', 'a0'])
+ self.assertEqual(self._elem_tags(e[::-2]), ['a3', 'a1'])
+
+ def test_delslice(self):
+ e = self._make_elem_with_children(4)
+ del e[0:2]
+ self.assertEqual(self._subelem_tags(e), ['a2', 'a3'])
+
+ e = self._make_elem_with_children(4)
+ del e[0:]
+ self.assertEqual(self._subelem_tags(e), [])
+
+ e = self._make_elem_with_children(4)
+ del e[::-1]
+ self.assertEqual(self._subelem_tags(e), [])
+
+ e = self._make_elem_with_children(4)
+ del e[::-2]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'a2'])
+
+ e = self._make_elem_with_children(4)
+ del e[1::2]
+ self.assertEqual(self._subelem_tags(e), ['a0', 'a2'])
+
+ e = self._make_elem_with_children(2)
+ del e[::2]
+ self.assertEqual(self._subelem_tags(e), ['a1'])
+
+
+class IOTest(unittest.TestCase):
+ def tearDown(self):
+ unlink(TESTFN)
+
+ def test_encoding(self):
+ # Test encoding issues.
+ elem = ET.Element("tag")
+ elem.text = "abc"
+ self.assertEqual(serialize(elem), '<tag>abc</tag>')
+ self.assertEqual(serialize(elem, encoding="utf-8"),
+ b'<tag>abc</tag>')
+ self.assertEqual(serialize(elem, encoding="us-ascii"),
+ b'<tag>abc</tag>')
+ for enc in ("iso-8859-1", "utf-16", "utf-32"):
+ self.assertEqual(serialize(elem, encoding=enc),
+ ("<?xml version='1.0' encoding='%s'?>\n"
+ "<tag>abc</tag>" % enc).encode(enc))
+
+ elem = ET.Element("tag")
+ elem.text = "<&\"\'>"
+ self.assertEqual(serialize(elem), '<tag><&"\'></tag>')
+ self.assertEqual(serialize(elem, encoding="utf-8"),
+ b'<tag><&"\'></tag>')
+ self.assertEqual(serialize(elem, encoding="us-ascii"),
+ b'<tag><&"\'></tag>')
+ for enc in ("iso-8859-1", "utf-16", "utf-32"):
+ self.assertEqual(serialize(elem, encoding=enc),
+ ("<?xml version='1.0' encoding='%s'?>\n"
+ "<tag><&\"'></tag>" % enc).encode(enc))
+
+ elem = ET.Element("tag")
+ elem.attrib["key"] = "<&\"\'>"
+ self.assertEqual(serialize(elem), '<tag key="<&"\'>" />')
+ self.assertEqual(serialize(elem, encoding="utf-8"),
+ b'<tag key="<&"\'>" />')
+ self.assertEqual(serialize(elem, encoding="us-ascii"),
+ b'<tag key="<&"\'>" />')
+ for enc in ("iso-8859-1", "utf-16", "utf-32"):
+ self.assertEqual(serialize(elem, encoding=enc),
+ ("<?xml version='1.0' encoding='%s'?>\n"
+ "<tag key=\"<&"'>\" />" % enc).encode(enc))
+
+ elem = ET.Element("tag")
+ elem.text = '\xe5\xf6\xf6<>'
+ self.assertEqual(serialize(elem), '<tag>\xe5\xf6\xf6<></tag>')
+ self.assertEqual(serialize(elem, encoding="utf-8"),
+ b'<tag>\xc3\xa5\xc3\xb6\xc3\xb6<></tag>')
+ self.assertEqual(serialize(elem, encoding="us-ascii"),
+ b'<tag>åöö<></tag>')
+ for enc in ("iso-8859-1", "utf-16", "utf-32"):
+ self.assertEqual(serialize(elem, encoding=enc),
+ ("<?xml version='1.0' encoding='%s'?>\n"
+ "<tag>åöö<></tag>" % enc).encode(enc))
+
+ elem = ET.Element("tag")
+ elem.attrib["key"] = '\xe5\xf6\xf6<>'
+ self.assertEqual(serialize(elem), '<tag key="\xe5\xf6\xf6<>" />')
+ self.assertEqual(serialize(elem, encoding="utf-8"),
+ b'<tag key="\xc3\xa5\xc3\xb6\xc3\xb6<>" />')
+ self.assertEqual(serialize(elem, encoding="us-ascii"),
+ b'<tag key="åöö<>" />')
+ for enc in ("iso-8859-1", "utf-16", "utf-16le", "utf-16be", "utf-32"):
+ self.assertEqual(serialize(elem, encoding=enc),
+ ("<?xml version='1.0' encoding='%s'?>\n"
+ "<tag key=\"åöö<>\" />" % enc).encode(enc))
+
+ def test_write_to_filename(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ tree.write(TESTFN)
+ with open(TESTFN, 'rb') as f:
+ self.assertEqual(f.read(), b'''<site />''')
+
+ def test_write_to_text_file(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ with open(TESTFN, 'w', encoding='utf-8') as f:
+ tree.write(f, encoding='unicode')
+ self.assertFalse(f.closed)
+ with open(TESTFN, 'rb') as f:
+ self.assertEqual(f.read(), b'''<site />''')
+
+ def test_write_to_binary_file(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ with open(TESTFN, 'wb') as f:
+ tree.write(f)
+ self.assertFalse(f.closed)
+ with open(TESTFN, 'rb') as f:
+ self.assertEqual(f.read(), b'''<site />''')
+
+ def test_write_to_binary_file_with_bom(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ # test BOM writing to buffered file
+ with open(TESTFN, 'wb') as f:
+ tree.write(f, encoding='utf-16')
+ self.assertFalse(f.closed)
+ with open(TESTFN, 'rb') as f:
+ self.assertEqual(f.read(),
+ '''<?xml version='1.0' encoding='utf-16'?>\n'''
+ '''<site />'''.encode("utf-16"))
+ # test BOM writing to non-buffered file
+ with open(TESTFN, 'wb', buffering=0) as f:
+ tree.write(f, encoding='utf-16')
+ self.assertFalse(f.closed)
+ with open(TESTFN, 'rb') as f:
+ self.assertEqual(f.read(),
+ '''<?xml version='1.0' encoding='utf-16'?>\n'''
+ '''<site />'''.encode("utf-16"))
+
+ def test_read_from_stringio(self):
+ tree = ET.ElementTree()
+ stream = io.StringIO('''<?xml version="1.0"?><site></site>''')
+ tree.parse(stream)
+ self.assertEqual(tree.getroot().tag, 'site')
+
+ def test_write_to_stringio(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ stream = io.StringIO()
+ tree.write(stream, encoding='unicode')
+ self.assertEqual(stream.getvalue(), '''<site />''')
+
+ def test_read_from_bytesio(self):
+ tree = ET.ElementTree()
+ raw = io.BytesIO(b'''<?xml version="1.0"?><site></site>''')
+ tree.parse(raw)
+ self.assertEqual(tree.getroot().tag, 'site')
+
+ def test_write_to_bytesio(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ raw = io.BytesIO()
+ tree.write(raw)
+ self.assertEqual(raw.getvalue(), b'''<site />''')
+
+ class dummy:
+ pass
+
+ def test_read_from_user_text_reader(self):
+ stream = io.StringIO('''<?xml version="1.0"?><site></site>''')
+ reader = self.dummy()
+ reader.read = stream.read
+ tree = ET.ElementTree()
+ tree.parse(reader)
+ self.assertEqual(tree.getroot().tag, 'site')
+
+ def test_write_to_user_text_writer(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ stream = io.StringIO()
+ writer = self.dummy()
+ writer.write = stream.write
+ tree.write(writer, encoding='unicode')
+ self.assertEqual(stream.getvalue(), '''<site />''')
+
+ def test_read_from_user_binary_reader(self):
+ raw = io.BytesIO(b'''<?xml version="1.0"?><site></site>''')
+ reader = self.dummy()
+ reader.read = raw.read
+ tree = ET.ElementTree()
+ tree.parse(reader)
+ self.assertEqual(tree.getroot().tag, 'site')
+ tree = ET.ElementTree()
+
+ def test_write_to_user_binary_writer(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ raw = io.BytesIO()
+ writer = self.dummy()
+ writer.write = raw.write
+ tree.write(writer)
+ self.assertEqual(raw.getvalue(), b'''<site />''')
+
+ def test_write_to_user_binary_writer_with_bom(self):
+ tree = ET.ElementTree(ET.XML('''<site />'''))
+ raw = io.BytesIO()
+ writer = self.dummy()
+ writer.write = raw.write
+ writer.seekable = lambda: True
+ writer.tell = raw.tell
+ tree.write(writer, encoding="utf-16")
+ self.assertEqual(raw.getvalue(),
+ '''<?xml version='1.0' encoding='utf-16'?>\n'''
+ '''<site />'''.encode("utf-16"))
+
+ def test_tostringlist_invariant(self):
+ root = ET.fromstring('<tag>foo</tag>')
+ self.assertEqual(
+ ET.tostring(root, 'unicode'),
+ ''.join(ET.tostringlist(root, 'unicode')))
+ self.assertEqual(
+ ET.tostring(root, 'utf-16'),
+ b''.join(ET.tostringlist(root, 'utf-16')))
+
+
+class ParseErrorTest(unittest.TestCase):
+ def test_subclass(self):
+ self.assertIsInstance(ET.ParseError(), SyntaxError)
+
+ def _get_error(self, s):
+ try:
+ ET.fromstring(s)
+ except ET.ParseError as e:
+ return e
+
+ def test_error_position(self):
+ self.assertEqual(self._get_error('foo').position, (1, 0))
+ self.assertEqual(self._get_error('<tag>&foo;</tag>').position, (1, 5))
+ self.assertEqual(self._get_error('foobar<').position, (1, 6))
+
+ def test_error_code(self):
+ import xml.parsers.expat.errors as ERRORS
+ self.assertEqual(self._get_error('foo').code,
+ ERRORS.codes[ERRORS.XML_ERROR_SYNTAX])
+
+
+class KeywordArgsTest(unittest.TestCase):
+ # Test various issues with keyword arguments passed to ET.Element
+ # constructor and methods
+ def test_issue14818(self):
+ x = ET.XML("<a>foo</a>")
+ self.assertEqual(x.find('a', None),
+ x.find(path='a', namespaces=None))
+ self.assertEqual(x.findtext('a', None, None),
+ x.findtext(path='a', default=None, namespaces=None))
+ self.assertEqual(x.findall('a', None),
+ x.findall(path='a', namespaces=None))
+ self.assertEqual(list(x.iterfind('a', None)),
+ list(x.iterfind(path='a', namespaces=None)))
+
+ self.assertEqual(ET.Element('a').attrib, {})
+ elements = [
+ ET.Element('a', dict(href="#", id="foo")),
+ ET.Element('a', attrib=dict(href="#", id="foo")),
+ ET.Element('a', dict(href="#"), id="foo"),
+ ET.Element('a', href="#", id="foo"),
+ ET.Element('a', dict(href="#", id="foo"), href="#", id="foo"),
+ ]
+ for e in elements:
+ self.assertEqual(e.tag, 'a')
+ self.assertEqual(e.attrib, dict(href="#", id="foo"))
+
+ e2 = ET.SubElement(elements[0], 'foobar', attrib={'key1': 'value1'})
+ self.assertEqual(e2.attrib['key1'], 'value1')
+
+ with self.assertRaisesRegex(TypeError, 'must be dict, not str'):
+ ET.Element('a', "I'm not a dict")
+ with self.assertRaisesRegex(TypeError, 'must be dict, not str'):
+ ET.Element('a', attrib="I'm not a dict")
+
+# --------------------------------------------------------------------
+
+@unittest.skipUnless(pyET, 'only for the Python version')
+class NoAcceleratorTest(unittest.TestCase):
+ # Test that the C accelerator was not imported for pyET
+ def test_correct_import_pyET(self):
+ self.assertEqual(pyET.Element.__module__, 'xml.etree.ElementTree')
+ self.assertEqual(pyET.SubElement.__module__, 'xml.etree.ElementTree')
# --------------------------------------------------------------------
- Issue #15801: Make sure mappings passed to '%' formatting are actually
subscriptable.
-- Issue #15726: Fix incorrect bounds checking in PyState_FindModule.
- Patch by Robin Schreiber.
+Library
+-------
-- Issue #15604: Update uses of PyObject_IsTrue() to check for and handle
- errors correctly. Patch by Serhiy Storchaka.
++- Issue #15970: xml.etree.ElementTree now serializes correctly the empty HTML
++ elements 'meta' and 'param'.
+
-- Issue #13119: sys.stdout and sys.stderr are now using "\r\n" newline on
- Windows, as Python 2.
+- Issue #15842: the SocketIO.{readable,writable,seekable} methods now
+ raise ValueError when the file-like object is closed. Patch by Alessandro
+ Moura.
-- Issue #14579: Fix CVE-2012-2135: vulnerability in the utf-16 decoder after
- error handling. Patch by Serhiy Storchaka.
+- Issue #15882: Change _decimal to accept any coefficient tuple when
+ constructing infinities. This is done for backwards compatibility
+ with decimal.py: Infinity coefficients are undefined in _decimal
+ (in accordance with the specification).
-- Issue #15404: Refleak in PyMethodObject repr.
+- Issue #15876: Fix a refleak in the curses module: window.encoding.
-- Issue #15394: An issue in PyModule_Create that caused references to
- be leaked on some error paths has been fixed. Patch by Julia Lawall.
+- Issue #15881: Fixed atexit hook in multiprocessing. Original patch
+ by Chris McDonough.
-- Issue #15368: An issue that caused bytecode generation to be
- non-deterministic when using randomized hashing (-R) has been fixed.
+- Issue #15340: Fix importing the random module when /dev/urandom cannot
+ be opened. This was a regression caused by the hash randomization patch.
-- Issue #15020: The program name used to search for Python's path is now
- "python3" under Unix, not "python".
+- Issue #15841: The readable(), writable() and seekable() methods of BytesIO
+ and StringIO objects now raise ValueError when the object has been closed.
+ Patch by Alessandro Moura.
-- Issue #15033: Fix the exit status bug when modules invoked using -m swith,
- return the proper failure return value (1). Patch contributed by Jeff Knupp.
+- Issue #15447: Use subprocess.DEVNULL in webbrowser, instead of opening
+ os.devnull explicitly and leaving it open.
-- Issue #12268: File readline, readlines and read() or readall() methods
- no longer lose data when an underlying read system call is interrupted.
- IOError is no longer raised due to a read system call returning EINTR
- from within these methods.
+- Issue #15509: webbrowser.UnixBrowser no longer passes empty arguments to
+ Popen when %action substitutions produce empty strings.
-- Issue #15142: Fix reference leak when deallocating instances of types
- created using PyType_FromSpec().
+- Issue #12776,#11839: call argparse type function (specified by add_argument)
+ only once. Before, the type function was called twice in the case where the
+ default was specified and the argument was given as well. This was
+ especially problematic for the FileType type, as a default file would always
+ be opened, even if a file argument was specified on the command line.
-- Issue #10053: Don't close FDs when FileIO.__init__ fails. Loosely based on
- the work by Hirokazu Yamamoto.
+- Issue #15906: Fix a regression in argparse caused by the preceding change,
+ when action='append', type='str' and default=[].
-- Issue #14775: Fix a potential quadratic dict build-up due to the garbage
- collector repeatedly trying to untrack dicts.
+Extension Modules
+-----------------
-- Issue #14494: Fix __future__.py and its documentation to note that
- absolute imports are the default behavior in 3.0 instead of 2.7.
- Patch by Sven Marnach.
+Tests
+-----
-- Issue #14761: Fix potential leak on an error case in the import machinery.
+- Issue #15802: Fix test logic in TestMaildir.test_create_tmp. Patch
+ by Serhiy Storchaka.
-- Issue #14699: Fix calling the classmethod descriptor directly.
+- Issue #15557: Added a test suite for the webbrowser module, thanks
+ to Anton Barkovsky.
-- Issue #14433: Prevent msvcrt crash in interactive prompt when stdin
- is closed.
+Build
+-----
-- Issue #11603 (again): Setting __repr__ to __str__ now raises a RuntimeError
- when repr() or str() is called on such an object.
+- Issue #15819: Make sure we can build Python out-of-tree from a readonly
+ source directory. (Somewhat related to Issue #9860.)
-- Issue #14658: Fix binding a special method to a builtin implementation of a
- special method with a different name.
+Documentation
+-------------
-- Issue #14630: Fix a memory access bug for instances of a subclass of int
- with value 0.
+- Issue #11964: Document a change in v3.2 to the behavior of the indent
+ parameter of json encoding operations.
-- Issue #14612: Fix jumping around with blocks by setting f_lineno.
+Tools/Demos
+-----------
-- Issue #14607: Fix keyword-only arguments which started with ``__``.
-- Issue #13889: Check and (if necessary) set FPU control word before calling
- any of the dtoa.c string <-> float conversion functions, on MSVC builds of
- Python. This fixes issues when embedding Python in a Delphi app.
+What's New in Python 3.3.0?
+===========================
-- Issue #14474: Save and restore exception state in thread.start_new_thread()
- while writing error message if the thread leaves a unhandled exception.
+*Release date: XX-Sep-2012*
-- Issue #13019: Fix potential reference leaks in bytearray.extend(). Patch
- by Suman Saha.
+Core and Builtins
+-----------------
-- Issue #14378: Fix compiling ast.ImportFrom nodes with a "__future__" string as
- the module name that was not interned.
+Library
+-------
-- Issue #14331: Use significantly less stack space when importing modules by
- allocating path buffers on the heap instead of the stack.
-- Issue #14334: Prevent in a segfault in type.__getattribute__ when it was not
- passed strings.
+What's New in Python 3.3.0 Release Candidate 2?
+===============================================
-- Issue #1469629: Allow cycles through an object's __dict__ slot to be
- collected. (For example if ``x.__dict__ is x``).
+*Release date: 09-Sep-2012*
-- Issue #14172: Fix reference leak when marshalling a buffer-like object
- (other than a bytes object).
+Core and Builtins
+-----------------
-- Issue #13521: dict.setdefault() now does only one lookup for the given key,
- making it "atomic" for many purposes. Patch by Filip Gruszczyński.
+- Issue #13992: The trashcan mechanism is now thread-safe. This eliminates
+ sporadic crashes in multi-thread programs when several long deallocator
+ chains ran concurrently and involved subclasses of built-in container
+ types.
-- Issue #14471: Fix a possible buffer overrun in the winreg module.
+- Issue #15784: Modify OSError.__str__() to better distinguish between
+ errno error numbers and Windows error numbers.
+
+- Issue #15781: Fix two small race conditions in import's module locking.
Library
-------