Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-99181: fix except* on unhashable exceptions#99192

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 ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
iritkatriel merged 6 commits intopython:mainfromiritkatriel:unhashable
Nov 8, 2022
Merged
Show file tree
Hide file tree
Changes fromall commits
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
199 changes: 199 additions & 0 deletionsLib/test/test_except_star.py
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -1000,5 +1000,204 @@ def test_exc_info_restored(self):
self.assertEqual(sys.exc_info(), (None, None, None))


class TestExceptStar_WeirdLeafExceptions(ExceptStarTest):
# Test that except* works when leaf exceptions are
# unhashable or have a bad custom __eq__

class UnhashableExc(ValueError):
__hash__ = None

class AlwaysEqualExc(ValueError):
def __eq__(self, other):
return True

class NeverEqualExc(ValueError):
def __eq__(self, other):
return False

class BrokenEqualExc(ValueError):
def __eq__(self, other):
raise RuntimeError()

def setUp(self):
self.bad_types = [self.UnhashableExc,
self.AlwaysEqualExc,
self.NeverEqualExc,
self.BrokenEqualExc]

def except_type(self, eg, type):
match, rest = None, None
try:
try:
raise eg
except* type as e:
match = e
except Exception as e:
rest = e
return match, rest

def test_catch_unhashable_leaf_exception(self):
for Bad in self.bad_types:
with self.subTest(Bad):
eg = ExceptionGroup("eg", [TypeError(1), Bad(2)])
match, rest = self.except_type(eg, Bad)
self.assertExceptionIsLike(
match, ExceptionGroup("eg", [Bad(2)]))
self.assertExceptionIsLike(
rest, ExceptionGroup("eg", [TypeError(1)]))

def test_propagate_unhashable_leaf(self):
for Bad in self.bad_types:
with self.subTest(Bad):
eg = ExceptionGroup("eg", [TypeError(1), Bad(2)])
match, rest = self.except_type(eg, TypeError)
self.assertExceptionIsLike(
match, ExceptionGroup("eg", [TypeError(1)]))
self.assertExceptionIsLike(
rest, ExceptionGroup("eg", [Bad(2)]))

def test_catch_nothing_unhashable_leaf(self):
for Bad in self.bad_types:
with self.subTest(Bad):
eg = ExceptionGroup("eg", [TypeError(1), Bad(2)])
match, rest = self.except_type(eg, OSError)
self.assertIsNone(match)
self.assertExceptionIsLike(rest, eg)

def test_catch_everything_unhashable_leaf(self):
for Bad in self.bad_types:
with self.subTest(Bad):
eg = ExceptionGroup("eg", [TypeError(1), Bad(2)])
match, rest = self.except_type(eg, Exception)
self.assertExceptionIsLike(match, eg)
self.assertIsNone(rest)

def test_reraise_unhashable_leaf(self):
for Bad in self.bad_types:
with self.subTest(Bad):
eg = ExceptionGroup(
"eg", [TypeError(1), Bad(2), ValueError(3)])

try:
try:
raise eg
except* TypeError:
pass
except* Bad:
raise
except Exception as e:
exc = e

self.assertExceptionIsLike(
exc, ExceptionGroup("eg", [Bad(2), ValueError(3)]))


class TestExceptStar_WeirdExceptionGroupSubclass(ExceptStarTest):
# Test that except* works with exception groups that are
# unhashable or have a bad custom __eq__

class UnhashableEG(ExceptionGroup):
__hash__ = None

def derive(self, excs):
return type(self)(self.message, excs)

class AlwaysEqualEG(ExceptionGroup):
def __eq__(self, other):
return True

def derive(self, excs):
return type(self)(self.message, excs)

class NeverEqualEG(ExceptionGroup):
def __eq__(self, other):
return False

def derive(self, excs):
return type(self)(self.message, excs)

class BrokenEqualEG(ExceptionGroup):
def __eq__(self, other):
raise RuntimeError()

def derive(self, excs):
return type(self)(self.message, excs)

def setUp(self):
self.bad_types = [self.UnhashableEG,
self.AlwaysEqualEG,
self.NeverEqualEG,
self.BrokenEqualEG]

def except_type(self, eg, type):
match, rest = None, None
try:
try:
raise eg
except* type as e:
match = e
except Exception as e:
rest = e
return match, rest

def test_catch_some_unhashable_exception_group_subclass(self):
for BadEG in self.bad_types:
with self.subTest(BadEG):
eg = BadEG("eg",
[TypeError(1),
BadEG("nested", [ValueError(2)])])

match, rest = self.except_type(eg, TypeError)
self.assertExceptionIsLike(match, BadEG("eg", [TypeError(1)]))
self.assertExceptionIsLike(rest,
BadEG("eg", [BadEG("nested", [ValueError(2)])]))

def test_catch_none_unhashable_exception_group_subclass(self):
for BadEG in self.bad_types:
with self.subTest(BadEG):

eg = BadEG("eg",
[TypeError(1),
BadEG("nested", [ValueError(2)])])

match, rest = self.except_type(eg, OSError)
self.assertIsNone(match)
self.assertExceptionIsLike(rest, eg)

def test_catch_all_unhashable_exception_group_subclass(self):
for BadEG in self.bad_types:
with self.subTest(BadEG):

eg = BadEG("eg",
[TypeError(1),
BadEG("nested", [ValueError(2)])])

match, rest = self.except_type(eg, Exception)
self.assertExceptionIsLike(match, eg)
self.assertIsNone(rest)

def test_reraise_unhashable_eg(self):
for BadEG in self.bad_types:
with self.subTest(BadEG):

eg = BadEG("eg",
[TypeError(1), ValueError(2),
BadEG("nested", [ValueError(3), OSError(4)])])

try:
try:
raise eg
except* ValueError:
pass
except* OSError:
raise
except Exception as e:
exc = e

self.assertExceptionIsLike(
exc, BadEG("eg", [TypeError(1),
BadEG("nested", [OSError(4)])]))


if __name__ == '__main__':
unittest.main()
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
Fix failure in :keyword:`except* <except_star>` with unhashable exceptions.
43 changes: 26 additions & 17 deletionsObjects/exceptions.c
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -962,11 +962,11 @@ typedef enum {
EXCEPTION_GROUP_MATCH_BY_TYPE = 0,
/* A PyFunction returning True for matching exceptions */
EXCEPTION_GROUP_MATCH_BY_PREDICATE = 1,
/* A set of leaf exceptions to include in the result.
/* A set ofthe IDs ofleaf exceptions to include in the result.
* This matcher type is used internally by the interpreter
* to construct reraised exceptions.
*/
EXCEPTION_GROUP_MATCH_INSTANCES = 2
EXCEPTION_GROUP_MATCH_INSTANCE_IDS = 2
} _exceptiongroup_split_matcher_type;

static int
Expand DownExpand Up@@ -1024,10 +1024,16 @@ exceptiongroup_split_check_match(PyObject *exc,
Py_DECREF(exc_matches);
return is_true;
}
caseEXCEPTION_GROUP_MATCH_INSTANCES: {
caseEXCEPTION_GROUP_MATCH_INSTANCE_IDS: {
assert(PySet_Check(matcher_value));
if (!_PyBaseExceptionGroup_Check(exc)) {
return PySet_Contains(matcher_value, exc);
PyObject *exc_id = PyLong_FromVoidPtr(exc);
if (exc_id == NULL) {
return -1;
}
int res = PySet_Contains(matcher_value, exc_id);
Py_DECREF(exc_id);
return res;
}
return 0;
}
Expand DownExpand Up@@ -1212,32 +1218,35 @@ BaseExceptionGroup_subgroup(PyObject *self, PyObject *args)
}

static int
collect_exception_group_leaves(PyObject *exc, PyObject *leaves)
collect_exception_group_leaf_ids(PyObject *exc, PyObject *leaf_ids)
{
if (Py_IsNone(exc)) {
return 0;
}

assert(PyExceptionInstance_Check(exc));
assert(PySet_Check(leaves));
assert(PySet_Check(leaf_ids));

/* Add all leaf exceptions in exc to theleaves set */
/* AddIDs ofall leaf exceptions in exc to theleaf_ids set */

if (!_PyBaseExceptionGroup_Check(exc)) {
if (PySet_Add(leaves, exc) < 0) {
PyObject *exc_id = PyLong_FromVoidPtr(exc);
if (exc_id == NULL) {
return -1;
}
return 0;
int res = PySet_Add(leaf_ids, exc_id);
Py_DECREF(exc_id);
return res;
}
PyBaseExceptionGroupObject *eg = _PyBaseExceptionGroupObject_cast(exc);
Py_ssize_t num_excs = PyTuple_GET_SIZE(eg->excs);
/* recursive calls */
for (Py_ssize_t i = 0; i < num_excs; i++) {
PyObject *e = PyTuple_GET_ITEM(eg->excs, i);
if (_Py_EnterRecursiveCall(" incollect_exception_group_leaves")) {
if (_Py_EnterRecursiveCall(" incollect_exception_group_leaf_ids")) {
return -1;
}
int res =collect_exception_group_leaves(e,leaves);
int res =collect_exception_group_leaf_ids(e,leaf_ids);
_Py_LeaveRecursiveCall();
if (res < 0) {
return -1;
Expand All@@ -1258,8 +1267,8 @@ exception_group_projection(PyObject *eg, PyObject *keep)
assert(_PyBaseExceptionGroup_Check(eg));
assert(PyList_CheckExact(keep));

PyObject *leaves = PySet_New(NULL);
if (!leaves) {
PyObject *leaf_ids = PySet_New(NULL);
if (!leaf_ids) {
return NULL;
}

Expand All@@ -1268,18 +1277,18 @@ exception_group_projection(PyObject *eg, PyObject *keep)
PyObject *e = PyList_GET_ITEM(keep, i);
assert(e != NULL);
assert(_PyBaseExceptionGroup_Check(e));
if (collect_exception_group_leaves(e,leaves) < 0) {
Py_DECREF(leaves);
if (collect_exception_group_leaf_ids(e,leaf_ids) < 0) {
Py_DECREF(leaf_ids);
return NULL;
}
}

_exceptiongroup_split_result split_result;
bool construct_rest = false;
int err = exceptiongroup_split_recursive(
eg,EXCEPTION_GROUP_MATCH_INSTANCES, leaves,
eg,EXCEPTION_GROUP_MATCH_INSTANCE_IDS, leaf_ids,
construct_rest, &split_result);
Py_DECREF(leaves);
Py_DECREF(leaf_ids);
if (err < 0) {
return NULL;
}
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp