Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Ignore only AttributeError on attribute lookups in ElementTree.XMLPar…
…ser() and propagate all other exceptions.
  • Loading branch information
Stefan Behnel
Stefan Behnel committed Sep 13, 2017
commit e0b8790f8e123048c0ada330f9a192b10a2a4b88
23 changes: 23 additions & 0 deletions Lib/test/test_xml_etree.py
Original file line number Diff line number Diff line change
Expand Up @@ -2400,6 +2400,29 @@ def _check_sample1_element(self, e):
self.assertEqual(child.tail, 'tail')
self.assertEqual(child.attrib, {})

def test_exceptions(self):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't XMLParserTest be better place? In any case I think it is better to place this method at the end of the class, after testing normal cases.

And maybe use more specific method name? "test_builder_attribute_errors" or something like.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. I kept it in the test class because the exceptions are related to the behaviour of the TreeBuilder.

class RaisingBuilder:
def __init__(self, raise_in=None, what=ValueError):
self.raise_in = raise_in
self.what = what

def __getattr__(self, name):
if name == self.raise_in:
raise self.what(self.raise_in)
def handle(*args):
pass
return handle

ET.XMLParser(target=RaisingBuilder())
# cET also checks for 'close' and 'doctype', PyET does it only at need
for event in ('start', 'data', 'end', 'comment', 'pi'):
with self.assertRaisesRegex(ValueError, event):
ET.XMLParser(target=RaisingBuilder(event))

ET.XMLParser(target=RaisingBuilder(what=AttributeError))
for event in ('start', 'data', 'end', 'comment', 'pi'):
ET.XMLParser(target=RaisingBuilder(event, what=AttributeError))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Close the parser explicitly. Just for the case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. (Requires parsing something, though.)


def test_dummy_builder(self):
class BaseDummyBuilder:
def close(self):
Expand Down
32 changes: 25 additions & 7 deletions Modules/_elementtree.c
Original file line number Diff line number Diff line change
Expand Up @@ -3259,6 +3259,17 @@ xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
return (PyObject *)self;
}

static int
ignore_attribute_error(PyObject *value) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put this { on new line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

if (value == NULL) {
if (!PyErr_ExceptionMatches(PyExc_AttributeError)) {
return -1;
}
PyErr_Clear();
}
return 0;
}

/*[clinic input]
_elementtree.XMLParser.__init__

Expand Down Expand Up @@ -3313,19 +3324,26 @@ _elementtree_XMLParser___init___impl(XMLParserObject *self, PyObject *html,
self->target = target;

self->handle_start = PyObject_GetAttrString(target, "start");
PyErr_Clear();
if (ignore_attribute_error(self->handle_start))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New PEP 7 rules require braces even around simple if body.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. (Sad in this case, given the relation between "active" code and error handling code.)

return -1;
self->handle_data = PyObject_GetAttrString(target, "data");
PyErr_Clear();
if (ignore_attribute_error(self->handle_data))
return -1;
self->handle_end = PyObject_GetAttrString(target, "end");
PyErr_Clear();
if (ignore_attribute_error(self->handle_end))
return -1;
self->handle_comment = PyObject_GetAttrString(target, "comment");
PyErr_Clear();
if (ignore_attribute_error(self->handle_comment))
return -1;
self->handle_pi = PyObject_GetAttrString(target, "pi");
PyErr_Clear();
if (ignore_attribute_error(self->handle_pi))
return -1;
self->handle_close = PyObject_GetAttrString(target, "close");
PyErr_Clear();
if (ignore_attribute_error(self->handle_close))
return -1;
self->handle_doctype = PyObject_GetAttrString(target, "doctype");
PyErr_Clear();
if (ignore_attribute_error(self->handle_doctype))
return -1;

/* configure parser */
EXPAT(SetUserData)(self->parser, self);
Expand Down