From: Ezio Melotti Date: Wed, 19 Sep 2012 05:25:01 +0000 (+0300) Subject: #15970: merge with 3.2. X-Git-Tag: v3.3.1rc1~818^2^2~68^2~5 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ab9b661fdd4201e2471d7816dd5bf834385c9da2;p=python #15970: merge with 3.2. --- ab9b661fdd4201e2471d7816dd5bf834385c9da2 diff --cc Lib/test/test_xml_etree.py index da1ad09686,3092f3bc11..97d64fcf18 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@@ -1711,654 -1851,25 +1711,666 @@@ def check_issue10777() >>> 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('%s' % elems), method='html') - '

' - >>> serialize(ET.XML('%s' % elems.lower()), method='html') - '

' - >>> elems = ''.join('<%s>' % (elem, elem) for elem in empty_elems) - >>> serialize(ET.XML('%s' % elems), method='html') - '

' - >>> serialize(ET.XML('%s' % elems.lower()), method='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>' % (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("this is a paragraph...") + 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(''' + + + bedroom1 + bedroom2 + + nothing here + + + bedroom8 + + ''') + + 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 = ('' + 'text') + + sample2 = '''sometext''' + + 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(), ''' +

Example.

+ + text + texttail + + +
''') + + +class XMLParserTest(unittest.TestCase): + sample1 = '22' + sample2 = ('' + 'text') + + 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), 'abc') + self.assertEqual(serialize(elem, encoding="utf-8"), + b'abc') + self.assertEqual(serialize(elem, encoding="us-ascii"), + b'abc') + for enc in ("iso-8859-1", "utf-16", "utf-32"): + self.assertEqual(serialize(elem, encoding=enc), + ("\n" + "abc" % enc).encode(enc)) + + elem = ET.Element("tag") + elem.text = "<&\"\'>" + self.assertEqual(serialize(elem), '<&"\'>') + self.assertEqual(serialize(elem, encoding="utf-8"), + b'<&"\'>') + self.assertEqual(serialize(elem, encoding="us-ascii"), + b'<&"\'>') + for enc in ("iso-8859-1", "utf-16", "utf-32"): + self.assertEqual(serialize(elem, encoding=enc), + ("\n" + "<&\"'>" % enc).encode(enc)) + + elem = ET.Element("tag") + elem.attrib["key"] = "<&\"\'>" + self.assertEqual(serialize(elem), '') + self.assertEqual(serialize(elem, encoding="utf-8"), + b'') + self.assertEqual(serialize(elem, encoding="us-ascii"), + b'') + for enc in ("iso-8859-1", "utf-16", "utf-32"): + self.assertEqual(serialize(elem, encoding=enc), + ("\n" + "" % enc).encode(enc)) + + elem = ET.Element("tag") + elem.text = '\xe5\xf6\xf6<>' + self.assertEqual(serialize(elem), '\xe5\xf6\xf6<>') + self.assertEqual(serialize(elem, encoding="utf-8"), + b'\xc3\xa5\xc3\xb6\xc3\xb6<>') + self.assertEqual(serialize(elem, encoding="us-ascii"), + b'åöö<>') + for enc in ("iso-8859-1", "utf-16", "utf-32"): + self.assertEqual(serialize(elem, encoding=enc), + ("\n" + "åöö<>" % enc).encode(enc)) + + elem = ET.Element("tag") + elem.attrib["key"] = '\xe5\xf6\xf6<>' + self.assertEqual(serialize(elem), '') + self.assertEqual(serialize(elem, encoding="utf-8"), + b'') + self.assertEqual(serialize(elem, encoding="us-ascii"), + b'') + for enc in ("iso-8859-1", "utf-16", "utf-16le", "utf-16be", "utf-32"): + self.assertEqual(serialize(elem, encoding=enc), + ("\n" + "" % enc).encode(enc)) + + def test_write_to_filename(self): + tree = ET.ElementTree(ET.XML('''''')) + tree.write(TESTFN) + with open(TESTFN, 'rb') as f: + self.assertEqual(f.read(), b'''''') + + def test_write_to_text_file(self): + tree = ET.ElementTree(ET.XML('''''')) + 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'''''') + + def test_write_to_binary_file(self): + tree = ET.ElementTree(ET.XML('''''')) + with open(TESTFN, 'wb') as f: + tree.write(f) + self.assertFalse(f.closed) + with open(TESTFN, 'rb') as f: + self.assertEqual(f.read(), b'''''') + + def test_write_to_binary_file_with_bom(self): + tree = ET.ElementTree(ET.XML('''''')) + # 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(), + '''\n''' + ''''''.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(), + '''\n''' + ''''''.encode("utf-16")) + + def test_read_from_stringio(self): + tree = ET.ElementTree() + stream = io.StringIO('''''') + tree.parse(stream) + self.assertEqual(tree.getroot().tag, 'site') + + def test_write_to_stringio(self): + tree = ET.ElementTree(ET.XML('''''')) + stream = io.StringIO() + tree.write(stream, encoding='unicode') + self.assertEqual(stream.getvalue(), '''''') + + def test_read_from_bytesio(self): + tree = ET.ElementTree() + raw = io.BytesIO(b'''''') + tree.parse(raw) + self.assertEqual(tree.getroot().tag, 'site') + + def test_write_to_bytesio(self): + tree = ET.ElementTree(ET.XML('''''')) + raw = io.BytesIO() + tree.write(raw) + self.assertEqual(raw.getvalue(), b'''''') + + class dummy: + pass + + def test_read_from_user_text_reader(self): + stream = io.StringIO('''''') + 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('''''')) + stream = io.StringIO() + writer = self.dummy() + writer.write = stream.write + tree.write(writer, encoding='unicode') + self.assertEqual(stream.getvalue(), '''''') + + def test_read_from_user_binary_reader(self): + raw = io.BytesIO(b'''''') + 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('''''')) + raw = io.BytesIO() + writer = self.dummy() + writer.write = raw.write + tree.write(writer) + self.assertEqual(raw.getvalue(), b'''''') + + def test_write_to_user_binary_writer_with_bom(self): + tree = ET.ElementTree(ET.XML('''''')) + 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(), + '''\n''' + ''''''.encode("utf-16")) + + def test_tostringlist_invariant(self): + root = ET.fromstring('foo') + 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('&foo;').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("foo") + 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') # -------------------------------------------------------------------- diff --cc Misc/NEWS index 90e3cc188c,5113ec08ce..955a371ce1 --- a/Misc/NEWS +++ b/Misc/NEWS @@@ -29,102 -21,101 +29,105 @@@ Core and Builtin - 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 -------