Issue #14207: the ParseError exception raised by _elementtree was made
consistent to the one raised by the Python module (the 'code' attribute
was added).
In addition, the exception is now documented.
Added a test to check that ParseError has the required attributes, and
threw away the equivalent doctest which is no longer required.
diff --git a/Doc/library/xml.etree.elementtree.rst b/Doc/library/xml.etree.elementtree.rst
index 07ec22d..21a60ed 100644
--- a/Doc/library/xml.etree.elementtree.rst
+++ b/Doc/library/xml.etree.elementtree.rst
@@ -198,7 +198,6 @@
Element Objects
---------------
-
.. class:: Element(tag, attrib={}, **extra)
Element class. This class defines the Element interface, and provides a
@@ -643,6 +642,24 @@
>>> parser.close()
4
+Exceptions
+----------
+
+.. class:: ParseError
+
+ XML parse error, raised by the various parsing methods in this module when
+ parsing fails. The string representation of an instance of this exception
+ will contain a user-friendly error message. In addition, it will have
+ the following attributes available:
+
+ .. attribute:: code
+
+ A numeric error code from the expat parser. See the documentation of
+ :mod:`xml.parsers.expat` for the list of error codes and their meanings.
+
+ .. attribute:: position
+
+ A tuple of *line*, *column* numbers, specifying where the error occurred.
.. rubric:: Footnotes
diff --git a/Lib/test/test_xml_etree.py b/Lib/test/test_xml_etree.py
index 97fc690..c1dce04 100644
--- a/Lib/test/test_xml_etree.py
+++ b/Lib/test/test_xml_etree.py
@@ -1055,26 +1055,6 @@
'<document>text</document>'
"""
-def error(xml):
- """
-
- Test error handling.
-
- >>> issubclass(ET.ParseError, SyntaxError)
- True
- >>> error("foo").position
- (1, 0)
- >>> error("<tag>&foo;</tag>").position
- (1, 5)
- >>> error("foobar<").position
- (1, 6)
-
- """
- try:
- ET.XML(xml)
- except ET.ParseError:
- return sys.exc_info()[1]
-
def namespace():
"""
Test namespace issues.
@@ -2039,6 +2019,27 @@
self.assertEqual(tree.getroot().tag, 'site')
+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])
+
+
# --------------------------------------------------------------------
@@ -2091,6 +2092,7 @@
test_classes = [
ElementSlicingTest,
StringIOTest,
+ ParseErrorTest,
ElementTreeTest,
TreeBuilderTest]
if module is pyET:
diff --git a/Modules/_elementtree.c b/Modules/_elementtree.c
index 99935b9..a50a3e7 100644
--- a/Modules/_elementtree.c
+++ b/Modules/_elementtree.c
@@ -2177,13 +2177,18 @@
return value;
}
+/* Set the ParseError exception with the given parameters.
+ * If message is not NULL, it's used as the error string. Otherwise, the
+ * message string is the default for the given error_code.
+*/
static void
-expat_set_error(const char* message, int line, int column)
+expat_set_error(enum XML_Error error_code, int line, int column, char *message)
{
- PyObject *errmsg, *error, *position;
+ PyObject *errmsg, *error, *position, *code;
errmsg = PyUnicode_FromFormat("%s: line %d, column %d",
- message, line, column);
+ message ? message : EXPAT(ErrorString)(error_code),
+ line, column);
if (errmsg == NULL)
return;
@@ -2192,7 +2197,19 @@
if (!error)
return;
- /* add position attribute */
+ /* Add code and position attributes */
+ code = PyLong_FromLong((long)error_code);
+ if (!code) {
+ Py_DECREF(error);
+ return;
+ }
+ if (PyObject_SetAttrString(error, "code", code) == -1) {
+ Py_DECREF(error);
+ Py_DECREF(code);
+ return;
+ }
+ Py_DECREF(code);
+
position = Py_BuildValue("(ii)", line, column);
if (!position) {
Py_DECREF(error);
@@ -2244,9 +2261,10 @@
char message[128] = "undefined entity ";
strncat(message, data_in, data_len < 100?data_len:100);
expat_set_error(
- message,
+ XML_ERROR_UNDEFINED_ENTITY,
EXPAT(GetErrorLineNumber)(self->parser),
- EXPAT(GetErrorColumnNumber)(self->parser)
+ EXPAT(GetErrorColumnNumber)(self->parser),
+ message
);
}
@@ -2629,9 +2647,10 @@
if (!ok) {
expat_set_error(
- EXPAT(ErrorString)(EXPAT(GetErrorCode)(self->parser)),
+ EXPAT(GetErrorCode)(self->parser),
EXPAT(GetErrorLineNumber)(self->parser),
- EXPAT(GetErrorColumnNumber)(self->parser)
+ EXPAT(GetErrorColumnNumber)(self->parser),
+ NULL
);
return NULL;
}