Skip to content

Commit

Permalink
bpo-40824: Do not mask errors in __iter__ in "in" and the operator mo…
Browse files Browse the repository at this point in the history
…dule. (pythonGH-20537)

Unexpected errors in calling the __iter__ method are no longer
masked by TypeError in the "in" operator and functions
operator.contains(), operator.indexOf() and operator.countOf().
  • Loading branch information
serhiy-storchaka authored Jun 22, 2020
1 parent 4901ea9 commit cafe1b6
Show file tree
Hide file tree
Showing 4 changed files with 21 additions and 1 deletion.
7 changes: 7 additions & 0 deletions Lib/test/test_iter.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ def __getitem__(self, i):
return i
__iter__ = None

class BadIterableClass:
def __iter__(self):
raise ZeroDivisionError

# Main test suite

class TestCase(unittest.TestCase):
Expand Down Expand Up @@ -658,6 +662,7 @@ def test_in_and_not_in(self):

self.assertRaises(TypeError, lambda: 3 in 12)
self.assertRaises(TypeError, lambda: 3 not in map)
self.assertRaises(ZeroDivisionError, lambda: 3 in BadIterableClass())

d = {"one": 1, "two": 2, "three": 3, 1j: 2j}
for k in d:
Expand Down Expand Up @@ -740,6 +745,7 @@ def test_indexOf(self):

self.assertRaises(TypeError, indexOf, 42, 1)
self.assertRaises(TypeError, indexOf, indexOf, indexOf)
self.assertRaises(ZeroDivisionError, indexOf, BadIterableClass(), 1)

f = open(TESTFN, "w")
try:
Expand Down Expand Up @@ -1027,6 +1033,7 @@ def test_free_after_iterating(self):
def test_error_iter(self):
for typ in (DefaultIterClass, NoIterClass):
self.assertRaises(TypeError, iter, typ())
self.assertRaises(ZeroDivisionError, iter, BadIterableClass())


def test_main():
Expand Down
7 changes: 7 additions & 0 deletions Lib/test/test_operator.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ def __mul__(self, other):
def __rmul__(self, other):
return other * self.lst

class BadIterable:
def __iter__(self):
raise ZeroDivisionError


class OperatorTestCase:
def test_lt(self):
Expand Down Expand Up @@ -142,6 +146,7 @@ def test_countOf(self):
operator = self.module
self.assertRaises(TypeError, operator.countOf)
self.assertRaises(TypeError, operator.countOf, None, None)
self.assertRaises(ZeroDivisionError, operator.countOf, BadIterable(), 1)
self.assertEqual(operator.countOf([1, 2, 1, 3, 1, 4], 3), 1)
self.assertEqual(operator.countOf([1, 2, 1, 3, 1, 4], 5), 0)

Expand Down Expand Up @@ -176,6 +181,7 @@ def test_indexOf(self):
operator = self.module
self.assertRaises(TypeError, operator.indexOf)
self.assertRaises(TypeError, operator.indexOf, None, None)
self.assertRaises(ZeroDivisionError, operator.indexOf, BadIterable(), 1)
self.assertEqual(operator.indexOf([4, 3, 2, 1], 3), 1)
self.assertRaises(ValueError, operator.indexOf, [4, 3, 2, 1], 0)

Expand Down Expand Up @@ -258,6 +264,7 @@ def test_contains(self):
operator = self.module
self.assertRaises(TypeError, operator.contains)
self.assertRaises(TypeError, operator.contains, None, None)
self.assertRaises(ZeroDivisionError, operator.contains, BadIterable(), 1)
self.assertTrue(operator.contains(range(4), 2))
self.assertFalse(operator.contains(range(4), 5))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Unexpected errors in calling the ``__iter__`` method are no longer masked by
``TypeError`` in the :keyword:`in` operator and functions
:func:`~operator.contains`, :func:`~operator.indexOf` and
:func:`~operator.countOf` of the :mod:`operator` module.
4 changes: 3 additions & 1 deletion Objects/abstract.c
Original file line number Diff line number Diff line change
Expand Up @@ -2083,7 +2083,9 @@ _PySequence_IterSearch(PyObject *seq, PyObject *obj, int operation)

it = PyObject_GetIter(seq);
if (it == NULL) {
type_error("argument of type '%.200s' is not iterable", seq);
if (PyErr_ExceptionMatches(PyExc_TypeError)) {
type_error("argument of type '%.200s' is not iterable", seq);
}
return -1;
}

Expand Down

0 comments on commit cafe1b6

Please sign in to comment.