-
-
Notifications
You must be signed in to change notification settings - Fork 32.2k
bpo-18533: Avoid RuntimeError from repr() of recursive dictview #4823
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
Conversation
Hello, and thanks for your contribution! I'm a bot set up to make sure that the project can legally accept your contribution by verifying you have signed the PSF contributor agreement (CLA). Unfortunately we couldn't find an account corresponding to your GitHub username on bugs.python.org (b.p.o) to verify you have signed the CLA (this might be simply due to a missing "GitHub Name" entry in your b.p.o account settings). This is necessary for legal reasons before we can look at your contribution. Please follow the steps outlined in the CPython devguide to rectify this issue. Thanks again to your contribution and we look forward to looking at it! |
Hum, this bug has already been fixed in https://bugs.python.org/issue32137 by the commit 1fb72d2, no? |
The fix for bpo-32137 changed a segfault into a (I've just noticed that my commit message incorrectly refers to |
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.
LGTM. Just fix a style and add a news entry.
What about OrderedDict? Is it fixed by this change?
Objects/dictobject.c
Outdated
Py_ssize_t rc; | ||
|
||
rc = Py_ReprEnter((PyObject *)dv); | ||
if (rc != 0) |
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.
Add braces.
Objects/dictobject.c
Outdated
|
||
rc = Py_ReprEnter((PyObject *)dv); | ||
if (rc != 0) | ||
return rc > 0 ? PyUnicode_FromString("...") : NULL; | ||
|
||
seq = PySequence_List((PyObject *)dv); | ||
if (seq == NULL) |
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.
Add braces.
Lib/test/test_dictviews.py
Outdated
@@ -202,6 +203,18 @@ def test_items_set_operations(self): | |||
def test_recursive_repr(self): | |||
d = {} | |||
d[42] = d.values() | |||
r = repr(d) | |||
self.assertIsInstance(r, str) |
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.
Is this needed?
3822baa
to
cc8c16b
Compare
@serhiy-storchaka --- I've implemented those style requests, and added a news entry. Without this PR,
whereas with this PR:
The PR now adds a test for the new behaviour of |
Objects/dictobject.c
Outdated
rc = Py_ReprEnter((PyObject *)dv); | ||
if (rc != 0) { | ||
return rc > 0 ? PyUnicode_FromString("...") : NULL; | ||
} | ||
|
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.
I think empty lines are not needed after }
. }
itself adds enough vertical space.
Lib/test/test_collections.py
Outdated
|
||
class TestOrderedDict(unittest.TestCase): | ||
|
||
def test_recursive_repr(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.
Move this test into test_ordered_dict.py.
Lib/test/test_collections.py
Outdated
d = OrderedDict() | ||
d[42] = d.values() | ||
r = repr(d) | ||
self.assertEqual(r, 'OrderedDict([(42, odict_values([...]))])') |
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.
The concrete implementation is implementation dependent. For example in PyPy3 it is OrderedDict([(42, _OrderedDictValuesView(...))])
. I think it enough to test that repr() doesn't raise an exception in other implementations.
r = repr(d)
if test.support.check_impl_detail(cpython=True):
self.assertEqual(r, 'OrderedDict([(42, odict_values([...]))])')
dictview_repr(): Use a Py_ReprEnter() / Py_ReprLeave() pair to check for recursion, and produce "..." if so. test_recursive_repr(): Check for the a string rather than a RecursionError. (Test cannot be any tighter as contents are implementation-dependent.) test_deeply_nested_repr(): Add new test, replacing the original test_recursive_repr(). It checks that a RecursionError is raised in the case of a non-recursive but deeply nested structure. (Very similar to what test_repr_deep() in test/test_dict.py does for a normal dict.) OrderedDictTests: Add new test case, to test behaviour on OrderedDict instances containing their own values() or items().
cc8c16b
to
30dcf01
Compare
Thanks for feedback. New version pushed addressing comments:
Also word 'news' entry more concisely. |
Is there anything further I should be doing to help this along? The failing AppVeyor build looks unrelated as it's a failure in |
@terryjreedy — Thanks for bringing this up-to-date with |
I merged it, @bennorth. All the requirements were met and thanks for your contribution. |
Thanks @bennorth for the PR, and @orsenthil for merging it 🌮🎉.. I'm working now to backport this PR to: 3.6. |
Thanks @bennorth for the PR, and @orsenthil for merging it 🌮🎉.. I'm working now to backport this PR to: 2.7. |
GH-5348 is a backport of this pull request to the 3.6 branch. |
…thonGH-4823) dictview_repr(): Use a Py_ReprEnter() / Py_ReprLeave() pair to check for recursion, and produce "..." if so. test_recursive_repr(): Check for the string rather than a RecursionError. (Test cannot be any tighter as contents are implementation-dependent.) test_deeply_nested_repr(): Add new test, replacing the original test_recursive_repr(). It checks that a RecursionError is raised in the case of a non-recursive but deeply nested structure. (Very similar to what test_repr_deep() in test/test_dict.py does for a normal dict.) OrderedDictTests: Add new test case, to test behavior on OrderedDict instances containing their own values() or items(). (cherry picked from commit d7773d9)
Sorry, @bennorth and @orsenthil, I could not cleanly backport this to |
@orsenthil — thanks for merging. As suggested in comment on issue, I've created #5357 to backport this to 2.7. |
…-4823) dictview_repr(): Use a Py_ReprEnter() / Py_ReprLeave() pair to check for recursion, and produce "..." if so. test_recursive_repr(): Check for the string rather than a RecursionError. (Test cannot be any tighter as contents are implementation-dependent.) test_deeply_nested_repr(): Add new test, replacing the original test_recursive_repr(). It checks that a RecursionError is raised in the case of a non-recursive but deeply nested structure. (Very similar to what test_repr_deep() in test/test_dict.py does for a normal dict.) OrderedDictTests: Add new test case, to test behavior on OrderedDict instances containing their own values() or items(). (cherry picked from commit d7773d9)
…on#4823) dictview_repr(): Use a Py_ReprEnter() / Py_ReprLeave() pair to check for recursion, and produce "..." if so. test_recursive_repr(): Check for a string rather than a RuntimeError. (Test cannot be any tighter as contents are implementation-dependent.) test_deeply_nested_repr(): Add new test, replacing the original test_recursive_repr(). It checks that a RuntimeError is raised in the case of a non-recursive but deeply nested structure. OrderedDictTests: Add new test case, to test behavior on OrderedDict instances containing their own viewvalues() or viewitems(). This test passes without the patch to dictview_repr(), but it failed in (at least some versions of) Python 3, so including it here for completeness. (cherry picked from commit d7773d9)
(Created as per recent comment on bpo-18533. See issue for further background and discussion.)
dictview_repr()
: Use aPy_ReprEnter()
/Py_ReprLeave()
pair to check for recursion, and produce"..."
if so.test_recursive_repr()
: Check for the correct string (containing"..."
) rather than aRuntimeError
.test_deeply_nested_repr()
: Add new test, replacing the originaltest_recursive_repr()
. It checks that aRuntimeError
is raised in the case of a non-recursive but deeply nested structure. (Very similar to whattest_repr_deep()
intest/test_dict.py
does for a normal dict.)https://bugs.python.org/issue18533