Issue #19176: Fixed doctype() related bugs in C implementation of ElementTree.

A deprecation warning no longer issued by XMLParser subclass with default
doctype() method.  Direct call of doctype() now issues a warning.  Parser's
doctype() now is not called if target's doctype() is called.  Based on patch
by Martin Panter.
This commit is contained in:
Serhiy Storchaka 2015-06-29 23:11:51 +03:00
commit c28e985f3c
4 changed files with 100 additions and 22 deletions

View File

@ -12,6 +12,7 @@ import pickle
import sys import sys
import types import types
import unittest import unittest
import warnings
import weakref import weakref
from itertools import product from itertools import product
@ -2237,6 +2238,20 @@ class XMLParserTest(unittest.TestCase):
parser.feed(self.sample1) parser.feed(self.sample1)
self._check_sample_element(parser.close()) self._check_sample_element(parser.close())
def test_doctype_warning(self):
parser = ET.XMLParser()
with self.assertWarns(DeprecationWarning):
parser.doctype('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')
parser.feed('<html/>')
parser.close()
with warnings.catch_warnings():
warnings.simplefilter('error', DeprecationWarning)
parser = ET.XMLParser()
parser.feed(self.sample2)
parser.close()
def test_subclass_doctype(self): def test_subclass_doctype(self):
_doctype = None _doctype = None
class MyParserWithDoctype(ET.XMLParser): class MyParserWithDoctype(ET.XMLParser):
@ -2252,6 +2267,32 @@ class XMLParserTest(unittest.TestCase):
('html', '-//W3C//DTD XHTML 1.0 Transitional//EN', ('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd')) 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
_doctype = _doctype2 = None
with warnings.catch_warnings():
warnings.simplefilter('error', DeprecationWarning)
class DoctypeParser:
def doctype(self, name, pubid, system):
nonlocal _doctype2
_doctype2 = (name, pubid, system)
parser = MyParserWithDoctype(target=DoctypeParser())
parser.feed(self.sample2)
parser.close()
self.assertIsNone(_doctype)
self.assertEqual(_doctype2,
('html', '-//W3C//DTD XHTML 1.0 Transitional//EN',
'http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd'))
def test_inherited_doctype(self):
'''Ensure that ordinary usage is not deprecated (Issue 19176)'''
with warnings.catch_warnings():
warnings.simplefilter('error', DeprecationWarning)
class MyParserWithoutDoctype(ET.XMLParser):
pass
parser = MyParserWithoutDoctype()
parser.feed(self.sample2)
parser.close()
def test_parse_string(self): def test_parse_string(self):
parser = ET.XMLParser(target=ET.TreeBuilder()) parser = ET.XMLParser(target=ET.TreeBuilder())
parser.feed(self.sample3) parser.feed(self.sample3)

View File

@ -51,6 +51,12 @@ Core and Builtins
Library Library
------- -------
- Issue #19176: Fixed doctype() related bugs in C implementation of ElementTree.
A deprecation warning no longer issued by XMLParser subclass with default
doctype() method. Direct call of doctype() now issues a warning. Parser's
doctype() now is not called if target's doctype() is called. Based on patch
by Martin Panter.
- Issue #20387: Restore semantic round-trip correctness in tokenize/untokenize - Issue #20387: Restore semantic round-trip correctness in tokenize/untokenize
for tab-indented blocks. for tab-indented blocks.

View File

@ -2782,7 +2782,11 @@ typedef struct {
} XMLParserObject; } XMLParserObject;
#define XMLParser_CheckExact(op) (Py_TYPE(op) == &XMLParser_Type) static PyObject*
_elementtree_XMLParser_doctype(XMLParserObject* self, PyObject* args);
static PyObject *
_elementtree_XMLParser_doctype_impl(XMLParserObject *self, PyObject *name,
PyObject *pubid, PyObject *system);
/* helpers */ /* helpers */
@ -3182,20 +3186,22 @@ expat_start_doctype_handler(XMLParserObject *self,
doctype_name_obj, pubid_obj, sysid_obj); doctype_name_obj, pubid_obj, sysid_obj);
Py_CLEAR(res); Py_CLEAR(res);
} }
else {
/* Now see if the parser itself has a doctype method. If yes and it's /* Now see if the parser itself has a doctype method. If yes and it's
* a subclass, call it but warn about deprecation. If it's not a subclass * a custom method, call it but warn about deprecation. If it's only
* (i.e. vanilla XMLParser), do nothing. * the vanilla XMLParser method, do nothing.
*/ */
parser_doctype = PyObject_GetAttrString(self_pyobj, "doctype"); parser_doctype = PyObject_GetAttrString(self_pyobj, "doctype");
if (parser_doctype) { if (parser_doctype &&
if (!XMLParser_CheckExact(self_pyobj)) { !(PyCFunction_Check(parser_doctype) &&
if (PyErr_WarnEx(PyExc_DeprecationWarning, PyCFunction_GET_SELF(parser_doctype) == self_pyobj &&
"This method of XMLParser is deprecated. Define" PyCFunction_GET_FUNCTION(parser_doctype) ==
" doctype() method on the TreeBuilder target.", (PyCFunction) _elementtree_XMLParser_doctype)) {
1) < 0) { res = _elementtree_XMLParser_doctype_impl(self, doctype_name_obj,
pubid_obj, sysid_obj);
if (!res)
goto clear; goto clear;
} Py_DECREF(res);
res = PyObject_CallFunction(parser_doctype, "OOO", res = PyObject_CallFunction(parser_doctype, "OOO",
doctype_name_obj, pubid_obj, sysid_obj); doctype_name_obj, pubid_obj, sysid_obj);
Py_CLEAR(res); Py_CLEAR(res);
@ -3572,12 +3578,24 @@ _elementtree_XMLParser__parse_whole(XMLParserObject *self, PyObject *file)
/*[clinic input] /*[clinic input]
_elementtree.XMLParser.doctype _elementtree.XMLParser.doctype
name: object
pubid: object
system: object
/
[clinic start generated code]*/ [clinic start generated code]*/
static PyObject * static PyObject *
_elementtree_XMLParser_doctype_impl(XMLParserObject *self) _elementtree_XMLParser_doctype_impl(XMLParserObject *self, PyObject *name,
/*[clinic end generated code: output=d09fdb9c45f3a602 input=20d5e0febf902a2f]*/ PyObject *pubid, PyObject *system)
/*[clinic end generated code: output=10fb50c2afded88d input=84050276cca045e1]*/
{ {
if (PyErr_WarnEx(PyExc_DeprecationWarning,
"This method of XMLParser is deprecated. Define"
" doctype() method on the TreeBuilder target.",
1) < 0) {
return NULL;
}
Py_RETURN_NONE; Py_RETURN_NONE;
} }

View File

@ -619,20 +619,33 @@ PyDoc_STRVAR(_elementtree_XMLParser__parse_whole__doc__,
{"_parse_whole", (PyCFunction)_elementtree_XMLParser__parse_whole, METH_O, _elementtree_XMLParser__parse_whole__doc__}, {"_parse_whole", (PyCFunction)_elementtree_XMLParser__parse_whole, METH_O, _elementtree_XMLParser__parse_whole__doc__},
PyDoc_STRVAR(_elementtree_XMLParser_doctype__doc__, PyDoc_STRVAR(_elementtree_XMLParser_doctype__doc__,
"doctype($self, /)\n" "doctype($self, name, pubid, system, /)\n"
"--\n" "--\n"
"\n"); "\n");
#define _ELEMENTTREE_XMLPARSER_DOCTYPE_METHODDEF \ #define _ELEMENTTREE_XMLPARSER_DOCTYPE_METHODDEF \
{"doctype", (PyCFunction)_elementtree_XMLParser_doctype, METH_NOARGS, _elementtree_XMLParser_doctype__doc__}, {"doctype", (PyCFunction)_elementtree_XMLParser_doctype, METH_VARARGS, _elementtree_XMLParser_doctype__doc__},
static PyObject * static PyObject *
_elementtree_XMLParser_doctype_impl(XMLParserObject *self); _elementtree_XMLParser_doctype_impl(XMLParserObject *self, PyObject *name,
PyObject *pubid, PyObject *system);
static PyObject * static PyObject *
_elementtree_XMLParser_doctype(XMLParserObject *self, PyObject *Py_UNUSED(ignored)) _elementtree_XMLParser_doctype(XMLParserObject *self, PyObject *args)
{ {
return _elementtree_XMLParser_doctype_impl(self); PyObject *return_value = NULL;
PyObject *name;
PyObject *pubid;
PyObject *system;
if (!PyArg_UnpackTuple(args, "doctype",
3, 3,
&name, &pubid, &system))
goto exit;
return_value = _elementtree_XMLParser_doctype_impl(self, name, pubid, system);
exit:
return return_value;
} }
PyDoc_STRVAR(_elementtree_XMLParser__setevents__doc__, PyDoc_STRVAR(_elementtree_XMLParser__setevents__doc__,
@ -663,4 +676,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject *args)
exit: exit:
return return_value; return return_value;
} }
/*[clinic end generated code: output=119aed84c1545187 input=a9049054013a1b77]*/ /*[clinic end generated code: output=25b8bf7e7f2151ca input=a9049054013a1b77]*/