From 02ec92fa7b1dddc23d479ee0b87dc283793505a8 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Tue, 24 Jul 2018 12:03:34 +0300 Subject: bpo-29209: Remove old-deprecated features in ElementTree. (GH-6769) Also make getchildren() and getiterator() emitting a DeprecationWarning instead of PendingDeprecationWarning. --- Doc/library/xml.etree.elementtree.rst | 25 +++---- Doc/whatsnew/3.8.rst | 20 +++++ Lib/test/test_xml_etree.py | 31 ++------ Lib/xml/etree/ElementTree.py | 40 +++------- .../2018-05-12-13-06-41.bpo-29209.h5RxYy.rst | 6 ++ Modules/_elementtree.c | 87 ++++------------------ Modules/clinic/_elementtree.c.h | 46 ++---------- 7 files changed, 73 insertions(+), 182 deletions(-) create mode 100644 Misc/NEWS.d/next/Library/2018-05-12-13-06-41.bpo-29209.h5RxYy.rst diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst index 6298f13..0cb949a 100644 --- a/Doc/library/xml.etree.elementtree.rst +++ b/Doc/library/xml.etree.elementtree.rst @@ -772,13 +772,13 @@ Element Objects .. method:: getchildren() - .. deprecated:: 3.2 + .. deprecated-removed:: 3.2 3.9 Use ``list(elem)`` or iteration. .. method:: getiterator(tag=None) - .. deprecated:: 3.2 + .. deprecated-removed:: 3.2 3.9 Use method :meth:`Element.iter` instead. @@ -888,7 +888,7 @@ ElementTree Objects .. method:: getiterator(tag=None) - .. deprecated:: 3.2 + .. deprecated-removed:: 3.2 3.9 Use method :meth:`ElementTree.iter` instead. @@ -1050,20 +1050,20 @@ XMLParser Objects ^^^^^^^^^^^^^^^^^ -.. class:: XMLParser(html=0, target=None, encoding=None) +.. class:: XMLParser(*, target=None, encoding=None) This class is the low-level building block of the module. It uses :mod:`xml.parsers.expat` for efficient, event-based parsing of XML. It can be fed XML data incrementally with the :meth:`feed` method, and parsing events are translated to a push API - by invoking callbacks on the *target* object. If *target* is omitted, the standard :class:`TreeBuilder` is used. - The *html* argument was historically used for backwards compatibility and is - now deprecated. If *encoding* [1]_ is given, the value overrides the + If *encoding* [1]_ is given, the value overrides the encoding specified in the XML file. - .. deprecated:: 3.4 - The *html* argument. The remaining arguments should be passed via - keyword to prepare for the removal of the *html* argument. + .. versionchanged:: 3.8 + Parameters are now :ref:`keyword-only `. + The *html* argument no longer supported. + .. method:: close() @@ -1072,13 +1072,6 @@ XMLParser Objects this is the toplevel document element. - .. method:: doctype(name, pubid, system) - - .. deprecated:: 3.2 - Define the :meth:`TreeBuilder.doctype` method on a custom TreeBuilder - target. - - .. method:: feed(data) Feeds data to the parser. *data* is encoded data. diff --git a/Doc/whatsnew/3.8.rst b/Doc/whatsnew/3.8.rst index c6ecebc..3f51535 100644 --- a/Doc/whatsnew/3.8.rst +++ b/Doc/whatsnew/3.8.rst @@ -158,6 +158,11 @@ Build and C API Changes Deprecated ========== +* Deprecated methods ``getchildren()`` and ``getiterator()`` in + the :mod:`~xml.etree.ElementTree` module emit now a + :exc:`DeprecationWarning` instead of :exc:`PendingDeprecationWarning`. + They will be removed in Python 3.9. + (Contributed by Serhiy Storchaka in :issue:`29209`.) Removed @@ -173,6 +178,14 @@ Removed * ``filemode`` function is removed from :mod:`tarfile` module. It is not documented and deprecated since Python 3.3. +* The :class:`~xml.etree.ElementTree.XMLParser` constructor no longer accepts + the *html* argument. It never had effect and was deprecated in Python 3.4. + All other parameters are now :ref:`keyword-only `. + (Contributed by Serhiy Storchaka in :issue:`29209`.) + +* Removed the ``doctype()`` method of :class:`~xml.etree.ElementTree.XMLParser`. + (Contributed by Serhiy Storchaka in :issue:`29209`.) + Porting to Python 3.8 ===================== @@ -204,6 +217,13 @@ Changes in the Python API a database if it does not exist. (Contributed by Serhiy Storchaka in :issue:`32749`.) +* The ``doctype()`` method defined in a subclass of + :class:`~xml.etree.ElementTree.XMLParser` will no longer be called and will + cause emitting a :exc:`RuntimeWarning` instead of a :exc:`DeprecationWarning`. + Define the :meth:`doctype() ` + method on a target for handling an XML doctype declaration. + (Contributed by Serhiy Storchaka in :issue:`29209`.) + * A :exc:`RuntimeError` is now raised when the custom metaclass doesn't provide the ``__classcell__`` entry in the namespace passed to ``type.__new__``. A :exc:`DeprecationWarning` was emitted in Python diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py index e113975..a525290 100644 --- a/Lib/test/test_xml_etree.py +++ b/Lib/test/test_xml_etree.py @@ -706,7 +706,7 @@ class ElementTreeTest(unittest.TestCase): # Element.getchildren() and ElementTree.getiterator() are deprecated. @checkwarnings(("This method will be removed in future versions. " "Use .+ instead.", - (DeprecationWarning, PendingDeprecationWarning))) + DeprecationWarning)) def test_getchildren(self): # Test Element.getchildren() @@ -2399,7 +2399,7 @@ class ElementIterTest(unittest.TestCase): # Element.getiterator() is deprecated. @checkwarnings(("This method will be removed in future versions. " - "Use .+ instead.", PendingDeprecationWarning)) + "Use .+ instead.", DeprecationWarning)) def test_getiterator(self): doc = ET.XML(''' @@ -2605,14 +2605,6 @@ class XMLParserTest(unittest.TestCase): 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. - with self.assertWarnsRegex(DeprecationWarning, r'\bhtml\b'): - 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', target=ET.TreeBuilder()) parser2.feed(self.sample1) @@ -2626,13 +2618,6 @@ class XMLParserTest(unittest.TestCase): 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('') - parser.close() - with warnings.catch_warnings(): warnings.simplefilter('error', DeprecationWarning) parser = ET.XMLParser() @@ -2642,21 +2627,20 @@ class XMLParserTest(unittest.TestCase): def test_subclass_doctype(self): _doctype = None class MyParserWithDoctype(ET.XMLParser): - def doctype(self, name, pubid, system): + def doctype(self, *args, **kwargs): nonlocal _doctype - _doctype = (name, pubid, system) + _doctype = (args, kwargs) parser = MyParserWithDoctype() - with self.assertWarns(DeprecationWarning): + with self.assertWarnsRegex(RuntimeWarning, 'doctype'): 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')) + self.assertIsNone(_doctype) _doctype = _doctype2 = None with warnings.catch_warnings(): warnings.simplefilter('error', DeprecationWarning) + warnings.simplefilter('error', RuntimeWarning) class DoctypeParser: def doctype(self, name, pubid, system): nonlocal _doctype2 @@ -2674,6 +2658,7 @@ class XMLParserTest(unittest.TestCase): '''Ensure that ordinary usage is not deprecated (Issue 19176)''' with warnings.catch_warnings(): warnings.simplefilter('error', DeprecationWarning) + warnings.simplefilter('error', RuntimeWarning) class MyParserWithoutDoctype(ET.XMLParser): pass parser = MyParserWithoutDoctype() diff --git a/Lib/xml/etree/ElementTree.py b/Lib/xml/etree/ElementTree.py index 8727704..371b371 100644 --- a/Lib/xml/etree/ElementTree.py +++ b/Lib/xml/etree/ElementTree.py @@ -412,11 +412,10 @@ class Element: # compatibility def getiterator(self, tag=None): - # Change for a DeprecationWarning in 1.4 warnings.warn( "This method will be removed in future versions. " "Use 'elem.iter()' or 'list(elem.iter())' instead.", - PendingDeprecationWarning, stacklevel=2 + DeprecationWarning, stacklevel=2 ) return list(self.iter(tag)) @@ -622,11 +621,10 @@ class ElementTree: # compatibility def getiterator(self, tag=None): - # Change for a DeprecationWarning in 1.4 warnings.warn( "This method will be removed in future versions. " "Use 'tree.iter()' or 'list(tree.iter())' instead.", - PendingDeprecationWarning, stacklevel=2 + DeprecationWarning, stacklevel=2 ) return list(self.iter(tag)) @@ -1431,13 +1429,11 @@ class TreeBuilder: self._tail = 1 return self._last -_sentinel = ['sentinel'] # also see ElementTree and TreeBuilder class XMLParser: """Element structure builder for XML source data based on the expat parser. - *html* are predefined HTML entities (deprecated and not supported), *target* is an optional target object which defaults to an instance of the standard TreeBuilder class, *encoding* is an optional encoding string which if given, overrides the encoding specified in the XML file: @@ -1445,11 +1441,7 @@ class XMLParser: """ - def __init__(self, html=_sentinel, target=None, encoding=None): - if html is not _sentinel: - warnings.warn( - "The html argument of XMLParser() is deprecated", - DeprecationWarning, stacklevel=2) + def __init__(self, *, target=None, encoding=None): try: from xml.parsers import expat except ImportError: @@ -1602,27 +1594,13 @@ class XMLParser: return if hasattr(self.target, "doctype"): self.target.doctype(name, pubid, system[1:-1]) - elif self.doctype != self._XMLParser__doctype: - # warn about deprecated call - self._XMLParser__doctype(name, pubid, system[1:-1]) - self.doctype(name, pubid, system[1:-1]) - self._doctype = None - - def doctype(self, name, pubid, system): - """(Deprecated) Handle doctype declaration - - *name* is the Doctype name, *pubid* is the public identifier, - and *system* is the system identifier. + elif hasattr(self, "doctype"): + warnings.warn( + "The doctype() method of XMLParser is ignored. " + "Define doctype() method on the TreeBuilder target.", + RuntimeWarning) - """ - warnings.warn( - "This method of XMLParser is deprecated. Define doctype() " - "method on the TreeBuilder target.", - DeprecationWarning, - ) - - # sentinel, if doctype is redefined in a subclass - __doctype = doctype + self._doctype = None def feed(self, data): """Feed encoded data to parser.""" diff --git a/Misc/NEWS.d/next/Library/2018-05-12-13-06-41.bpo-29209.h5RxYy.rst b/Misc/NEWS.d/next/Library/2018-05-12-13-06-41.bpo-29209.h5RxYy.rst new file mode 100644 index 0000000..78d9b0c --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-05-12-13-06-41.bpo-29209.h5RxYy.rst @@ -0,0 +1,6 @@ +Removed the ``doctype()`` method and the *html* parameter of the constructor +of :class:`~xml.etree.ElementTree.XMLParser`. The ``doctype()`` method +defined in a subclass will no longer be called. Deprecated methods +``getchildren()`` and ``getiterator()`` in the :mod:`~xml.etree.ElementTree` +module emit now a :exc:`DeprecationWarning` instead of +:exc:`PendingDeprecationWarning`. diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c index 1dfdb3c..1500a6d 100644 --- a/Modules/_elementtree.c +++ b/Modules/_elementtree.c @@ -1429,8 +1429,7 @@ static PyObject * _elementtree_Element_getiterator_impl(ElementObject *self, PyObject *tag) /*[clinic end generated code: output=cb69ff4a3742dfa1 input=500da1a03f7b9e28]*/ { - /* Change for a DeprecationWarning in 1.4 */ - if (PyErr_WarnEx(PyExc_PendingDeprecationWarning, + if (PyErr_WarnEx(PyExc_DeprecationWarning, "This method will be removed in future versions. " "Use 'tree.iter()' or 'list(tree.iter())' instead.", 1) < 0) { @@ -2770,12 +2769,6 @@ typedef struct { } XMLParserObject; -static PyObject* -_elementtree_XMLParser_doctype(XMLParserObject *self, PyObject *const *args, Py_ssize_t nargs); -static PyObject * -_elementtree_XMLParser_doctype_impl(XMLParserObject *self, PyObject *name, - PyObject *pubid, PyObject *system); - /* helpers */ LOCAL(PyObject*) @@ -3139,10 +3132,9 @@ expat_start_doctype_handler(XMLParserObject *self, const XML_Char *pubid, int has_internal_subset) { - PyObject *self_pyobj = (PyObject *)self; + _Py_IDENTIFIER(doctype); PyObject *doctype_name_obj, *sysid_obj, *pubid_obj; - PyObject *parser_doctype = NULL; - PyObject *res = NULL; + PyObject *res; if (PyErr_Occurred()) return; @@ -3179,33 +3171,15 @@ expat_start_doctype_handler(XMLParserObject *self, res = PyObject_CallFunctionObjArgs(self->handle_doctype, doctype_name_obj, pubid_obj, sysid_obj, NULL); - Py_CLEAR(res); + Py_XDECREF(res); } - else { - /* Now see if the parser itself has a doctype method. If yes and it's - * a custom method, call it but warn about deprecation. If it's only - * the vanilla XMLParser method, do nothing. - */ - parser_doctype = PyObject_GetAttrString(self_pyobj, "doctype"); - if (parser_doctype && - !(PyCFunction_Check(parser_doctype) && - PyCFunction_GET_SELF(parser_doctype) == self_pyobj && - PyCFunction_GET_FUNCTION(parser_doctype) == - (PyCFunction) _elementtree_XMLParser_doctype)) { - res = _elementtree_XMLParser_doctype_impl(self, doctype_name_obj, - pubid_obj, sysid_obj); - if (!res) - goto clear; - Py_DECREF(res); - res = PyObject_CallFunctionObjArgs(parser_doctype, - doctype_name_obj, pubid_obj, - sysid_obj, NULL); - Py_CLEAR(res); - } + else if (_PyObject_LookupAttrId((PyObject *)self, &PyId_doctype, &res) > 0) { + (void)PyErr_WarnEx(PyExc_RuntimeWarning, + "The doctype() method of XMLParser is ignored. " + "Define doctype() method on the TreeBuilder target.", + 1); } -clear: - Py_XDECREF(parser_doctype); Py_DECREF(doctype_name_obj); Py_DECREF(pubid_obj); Py_DECREF(sysid_obj); @@ -3269,25 +3243,17 @@ ignore_attribute_error(PyObject *value) /*[clinic input] _elementtree.XMLParser.__init__ - html: object = NULL + * target: object = NULL encoding: str(accept={str, NoneType}) = NULL [clinic start generated code]*/ static int -_elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html, - PyObject *target, const char *encoding) -/*[clinic end generated code: output=d6a16c63dda54441 input=155bc5695baafffd]*/ -{ - if (html != NULL) { - if (PyErr_WarnEx(PyExc_DeprecationWarning, - "The html argument of XMLParser() is deprecated", - 1) < 0) { - return -1; - } - } - +_elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target, + const char *encoding) +/*[clinic end generated code: output=3ae45ec6cdf344e4 input=96288fcba916cfce]*/ +{ self->entity = PyDict_New(); if (!self->entity) return -1; @@ -3616,30 +3582,6 @@ _elementtree_XMLParser__parse_whole(XMLParserObject *self, PyObject *file) } /*[clinic input] -_elementtree.XMLParser.doctype - - name: object - pubid: object - system: object - / - -[clinic start generated code]*/ - -static PyObject * -_elementtree_XMLParser_doctype_impl(XMLParserObject *self, PyObject *name, - 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; -} - -/*[clinic input] _elementtree.XMLParser._setevents events_queue: object @@ -3923,7 +3865,6 @@ static PyMethodDef xmlparser_methods[] = { _ELEMENTTREE_XMLPARSER_CLOSE_METHODDEF _ELEMENTTREE_XMLPARSER__PARSE_WHOLE_METHODDEF _ELEMENTTREE_XMLPARSER__SETEVENTS_METHODDEF - _ELEMENTTREE_XMLPARSER_DOCTYPE_METHODDEF {NULL, NULL} }; diff --git a/Modules/clinic/_elementtree.c.h b/Modules/clinic/_elementtree.c.h index 6b55887..78b9be8 100644 --- a/Modules/clinic/_elementtree.c.h +++ b/Modules/clinic/_elementtree.c.h @@ -632,24 +632,23 @@ exit: } static int -_elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html, - PyObject *target, const char *encoding); +_elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *target, + const char *encoding); static int _elementtree_XMLParser___init__(PyObject *self, PyObject *args, PyObject *kwargs) { int return_value = -1; - static const char * const _keywords[] = {"html", "target", "encoding", NULL}; - static _PyArg_Parser _parser = {"|OOz:XMLParser", _keywords, 0}; - PyObject *html = NULL; + static const char * const _keywords[] = {"target", "encoding", NULL}; + static _PyArg_Parser _parser = {"|$Oz:XMLParser", _keywords, 0}; PyObject *target = NULL; const char *encoding = NULL; if (!_PyArg_ParseTupleAndKeywordsFast(args, kwargs, &_parser, - &html, &target, &encoding)) { + &target, &encoding)) { goto exit; } - return_value = _elementtree_XMLParser___init___impl((XMLParserObject *)self, html, target, encoding); + return_value = _elementtree_XMLParser___init___impl((XMLParserObject *)self, target, encoding); exit: return return_value; @@ -688,37 +687,6 @@ PyDoc_STRVAR(_elementtree_XMLParser__parse_whole__doc__, #define _ELEMENTTREE_XMLPARSER__PARSE_WHOLE_METHODDEF \ {"_parse_whole", (PyCFunction)_elementtree_XMLParser__parse_whole, METH_O, _elementtree_XMLParser__parse_whole__doc__}, -PyDoc_STRVAR(_elementtree_XMLParser_doctype__doc__, -"doctype($self, name, pubid, system, /)\n" -"--\n" -"\n"); - -#define _ELEMENTTREE_XMLPARSER_DOCTYPE_METHODDEF \ - {"doctype", (PyCFunction)_elementtree_XMLParser_doctype, METH_FASTCALL, _elementtree_XMLParser_doctype__doc__}, - -static PyObject * -_elementtree_XMLParser_doctype_impl(XMLParserObject *self, PyObject *name, - PyObject *pubid, PyObject *system); - -static PyObject * -_elementtree_XMLParser_doctype(XMLParserObject *self, PyObject *const *args, Py_ssize_t nargs) -{ - PyObject *return_value = NULL; - PyObject *name; - PyObject *pubid; - PyObject *system; - - if (!_PyArg_UnpackStack(args, nargs, "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__, "_setevents($self, events_queue, events_to_report=None, /)\n" "--\n" @@ -749,4 +717,4 @@ _elementtree_XMLParser__setevents(XMLParserObject *self, PyObject *const *args, exit: return return_value; } -/*[clinic end generated code: output=c5a85a88bbb5cc06 input=a9049054013a1b77]*/ +/*[clinic end generated code: output=1bff22415aabb78b input=a9049054013a1b77]*/ -- cgit v0.12