From 10ee2e90cdacf816c68cf8e44c4976dbc87b83bc Mon Sep 17 00:00:00 2001 From: Denis Laxalde Date: Thu, 28 Oct 2021 10:13:39 +0200 Subject: [PATCH] Raise a TypeError when slice-setting a Multirange with a non-iterable value The type declaration is: def __setitem__(index: slice, value: Iterable[Range[T]]) -> None: ... so passing a non-iterable value should not be allowed. However, the method implementation makes a special-case for non-iterable values with a slice. In accordance with MutableSequence behavior, e.g.: >>> x = [1, 2, 3] >>> x[1:3] = 0 Traceback (most recent call last): File "", line 1, in TypeError: can only assign an iterable it seems more correct to raise a TypeError. --- docs/news.rst | 2 ++ psycopg/psycopg/types/multirange.py | 7 +++---- tests/types/test_multirange.py | 5 ++++- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/docs/news.rst b/docs/news.rst index 28348aae4..832a1523a 100644 --- a/docs/news.rst +++ b/docs/news.rst @@ -15,6 +15,8 @@ Psycopg 3.0.2 - Fix type hint for `sql.SQL.join()` (:ticket:`#127`). - Fix type hint for `Connection.notifies()` (:ticket:`#128`). +- Fix call to `MultiRange.__setitem__()` with a non-iterable value and a + slice, now raising a `TypeError` (:ticket:`#129`). Psycopg 3.0.1 diff --git a/psycopg/psycopg/types/multirange.py b/psycopg/psycopg/types/multirange.py index 7824e230e..9d6763c6f 100644 --- a/psycopg/psycopg/types/multirange.py +++ b/psycopg/psycopg/types/multirange.py @@ -79,11 +79,10 @@ class Multirange(MutableSequence[Range[T]]): if isinstance(index, int): self._check_type(value) self._ranges[index] = self._check_type(value) + elif not isinstance(value, Iterable): + raise TypeError("can only assign an iterable") else: - if isinstance(value, Iterable): - value = map(self._check_type, value) - else: - value = [self._check_type(value)] + value = map(self._check_type, value) self._ranges[index] = value def __delitem__(self, index: Union[int, slice]) -> None: diff --git a/tests/types/test_multirange.py b/tests/types/test_multirange.py index 7721f63a8..e8f05dfc6 100644 --- a/tests/types/test_multirange.py +++ b/tests/types/test_multirange.py @@ -67,7 +67,10 @@ class TestMultirangeObject: assert mr == Multirange([Range(10, 20), Range(31, 41), Range(51, 61)]) mr = Multirange([Range(10, 20), Range(30, 40), Range(50, 60)]) - mr[1:3] = Range(31, 41) + with pytest.raises(TypeError, match="can only assign an iterable"): + mr[1:3] = Range(31, 41) # type: ignore[call-overload] + + mr[1:3] = [Range(31, 41)] assert mr == Multirange([Range(10, 20), Range(31, 41)]) def test_delitem(self): -- 2.47.2