Repository navigation
bpo-31455: avoid calling "PyObject_GetAttrString()" with a live exception set #3545
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
a9a6875
e0b8790
5253d56
3e1c77b
8cb95c6
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
…ser() and propagate all other exceptions.
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2400,6 +2400,29 @@ def _check_sample1_element(self, e): | |
| self.assertEqual(child.tail, 'tail') | ||
| self.assertEqual(child.attrib, {}) | ||
|
|
||
| def test_exceptions(self): | ||
| 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)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Close the parser explicitly. Just for the case.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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): | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3259,6 +3259,17 @@ xmlparser_new(PyTypeObject *type, PyObject *args, PyObject *kwds) | |
| return (PyObject *)self; | ||
| } | ||
|
|
||
| static int | ||
| ignore_attribute_error(PyObject *value) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put this
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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__ | ||
|
|
||
|
|
@@ -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)) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. New PEP 7 rules require braces even around simple
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't
XMLParserTestbe 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.
There was a problem hiding this comment.
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.