]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
restore functionality in list
authorFederico Caselli <cfederico87@gmail.com>
Thu, 14 Aug 2025 22:05:07 +0000 (00:05 +0200)
committerFederico Caselli <cfederico87@gmail.com>
Mon, 18 Aug 2025 21:56:37 +0000 (23:56 +0200)
Fixed issue caused by an unwanted functional change while typing
the :class:`.MutableList` class.
This change also reverts all other functional changes done in
the same change, commit ba0e508141206efc55cdab91df21c18e7dd63c80

Fixes: #12802
Change-Id: I007aa86aec881241ea42ce59d1b078cf8c6829bb
(cherry picked from commit a93d18357f7080b8e52f4e02983b6e50e33212ed)

doc/build/changelog/unreleased_20/12802.rst [new file with mode: 0644]
lib/sqlalchemy/ext/mutable.py
test/ext/test_mutable.py

diff --git a/doc/build/changelog/unreleased_20/12802.rst b/doc/build/changelog/unreleased_20/12802.rst
new file mode 100644 (file)
index 0000000..752326b
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, ext
+    :tickets: 12802
+
+    Fixed issue caused by an unwanted functional change while typing
+    the :class:`.MutableList` class.
+    This change also reverts all other functional changes done in
+    the same change.
index 3d568fc98926b893de4ed8906f31841458aaaeef..3a1fa4d6dacee04bfcf240ef79deed291cf990c3 100644 (file)
@@ -397,7 +397,6 @@ from weakref import WeakKeyDictionary
 from .. import event
 from .. import inspect
 from .. import types
-from .. import util
 from ..orm import Mapper
 from ..orm._typing import _ExternalEntityType
 from ..orm._typing import _O
@@ -416,7 +415,6 @@ from ..sql.schema import Column
 from ..sql.type_api import TypeEngine
 from ..util import memoized_property
 from ..util.typing import SupportsIndex
-from ..util.typing import TypeGuard
 
 _KT = TypeVar("_KT")  # Key type.
 _VT = TypeVar("_VT")  # Value type.
@@ -814,7 +812,7 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
 
     def __setitem__(self, key: _KT, value: _VT) -> None:
         """Detect dictionary set events and emit change events."""
-        super().__setitem__(key, value)
+        dict.__setitem__(self, key, value)
         self.changed()
 
     if TYPE_CHECKING:
@@ -833,17 +831,17 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
     else:
 
         def setdefault(self, *arg):  # noqa: F811
-            result = super().setdefault(*arg)
+            result = dict.setdefault(self, *arg)
             self.changed()
             return result
 
     def __delitem__(self, key: _KT) -> None:
         """Detect dictionary del events and emit change events."""
-        super().__delitem__(key)
+        dict.__delitem__(self, key)
         self.changed()
 
     def update(self, *a: Any, **kw: _VT) -> None:
-        super().update(*a, **kw)
+        dict.update(self, *a, **kw)
         self.changed()
 
     if TYPE_CHECKING:
@@ -861,17 +859,17 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
     else:
 
         def pop(self, *arg):  # noqa: F811
-            result = super().pop(*arg)
+            result = dict.pop(self, *arg)
             self.changed()
             return result
 
     def popitem(self) -> Tuple[_KT, _VT]:
-        result = super().popitem()
+        result = dict.popitem(self)
         self.changed()
         return result
 
     def clear(self) -> None:
-        super().clear()
+        dict.clear(self)
         self.changed()
 
     @classmethod
@@ -926,38 +924,29 @@ class MutableList(Mutable, List[_T]):
     def __setstate__(self, state: Iterable[_T]) -> None:
         self[:] = state
 
-    def is_scalar(self, value: _T | Iterable[_T]) -> TypeGuard[_T]:
-        return not util.is_non_string_iterable(value)
-
-    def is_iterable(self, value: _T | Iterable[_T]) -> TypeGuard[Iterable[_T]]:
-        return util.is_non_string_iterable(value)
-
     def __setitem__(
         self, index: SupportsIndex | slice, value: _T | Iterable[_T]
     ) -> None:
         """Detect list set events and emit change events."""
-        if isinstance(index, SupportsIndex) and self.is_scalar(value):
-            super().__setitem__(index, value)
-        elif isinstance(index, slice) and self.is_iterable(value):
-            super().__setitem__(index, value)
+        list.__setitem__(self, index, value)
         self.changed()
 
     def __delitem__(self, index: SupportsIndex | slice) -> None:
         """Detect list del events and emit change events."""
-        super().__delitem__(index)
+        list.__delitem__(self, index)
         self.changed()
 
     def pop(self, *arg: SupportsIndex) -> _T:
-        result = super().pop(*arg)
+        result = list.pop(self, *arg)
         self.changed()
         return result
 
     def append(self, x: _T) -> None:
-        super().append(x)
+        list.append(self, x)
         self.changed()
 
     def extend(self, x: Iterable[_T]) -> None:
-        super().extend(x)
+        list.extend(self, x)
         self.changed()
 
     def __iadd__(self, x: Iterable[_T]) -> MutableList[_T]:  # type: ignore[override,misc] # noqa: E501
@@ -965,23 +954,23 @@ class MutableList(Mutable, List[_T]):
         return self
 
     def insert(self, i: SupportsIndex, x: _T) -> None:
-        super().insert(i, x)
+        list.insert(self, i, x)
         self.changed()
 
     def remove(self, i: _T) -> None:
-        super().remove(i)
+        list.remove(self, i)
         self.changed()
 
     def clear(self) -> None:
-        super().clear()
+        list.clear(self)
         self.changed()
 
     def sort(self, **kw: Any) -> None:
-        super().sort(**kw)
+        list.sort(self, **kw)
         self.changed()
 
     def reverse(self) -> None:
-        super().reverse()
+        list.reverse(self)
         self.changed()
 
     @classmethod
@@ -1022,19 +1011,19 @@ class MutableSet(Mutable, Set[_T]):
     """
 
     def update(self, *arg: Iterable[_T]) -> None:
-        super().update(*arg)
+        set.update(self, *arg)
         self.changed()
 
     def intersection_update(self, *arg: Iterable[Any]) -> None:
-        super().intersection_update(*arg)
+        set.intersection_update(self, *arg)
         self.changed()
 
     def difference_update(self, *arg: Iterable[Any]) -> None:
-        super().difference_update(*arg)
+        set.difference_update(self, *arg)
         self.changed()
 
     def symmetric_difference_update(self, *arg: Iterable[_T]) -> None:
-        super().symmetric_difference_update(*arg)
+        set.symmetric_difference_update(self, *arg)
         self.changed()
 
     def __ior__(self, other: AbstractSet[_T]) -> MutableSet[_T]:  # type: ignore[override,misc] # noqa: E501
@@ -1054,24 +1043,24 @@ class MutableSet(Mutable, Set[_T]):
         return self
 
     def add(self, elem: _T) -> None:
-        super().add(elem)
+        set.add(self, elem)
         self.changed()
 
     def remove(self, elem: _T) -> None:
-        super().remove(elem)
+        set.remove(self, elem)
         self.changed()
 
     def discard(self, elem: _T) -> None:
-        super().discard(elem)
+        set.discard(self, elem)
         self.changed()
 
     def pop(self, *arg: Any) -> _T:
-        result = super().pop(*arg)
+        result = set.pop(self, *arg)
         self.changed()
         return result
 
     def clear(self) -> None:
-        super().clear()
+        set.clear(self)
         self.changed()
 
     @classmethod
index 423784777862374d652ffb54c5c62a06c5e77990..e83550aecd078f459d457a17e7690acc38dcaf65 100644 (file)
@@ -566,6 +566,11 @@ class _MutableListTestBase(_MutableListTestFixture):
 
         eq_(f1.data, ["three", "two"])
 
+        # test 12802
+        f1.data[1] = ["four", "two"]
+        sess.commit()
+        eq_(f1.data, ["three", ["four", "two"]])
+
     def test_in_place_slice_mutation_int(self):
         sess = fixture_session()
 
@@ -578,6 +583,11 @@ class _MutableListTestBase(_MutableListTestFixture):
 
         eq_(f1.data, [1, 5, 6, 4])
 
+        # test 12802
+        f1.data[1:3] = [9, 8, 7]
+        sess.commit()
+        eq_(f1.data, [1, 9, 8, 7, 4])
+
     def test_in_place_slice_mutation_str(self):
         sess = fixture_session()
 
@@ -680,6 +690,19 @@ class _MutableListTestBase(_MutableListTestFixture):
 
         eq_(f1.data, [1, 5, 2])
 
+    def test_insert2(self):
+        # test #12802
+        sess = fixture_session()
+
+        f1 = Foo(data=[1, 2])
+        sess.add(f1)
+        sess.commit()
+
+        f1.data.insert(1, [4, 2])
+        sess.commit()
+
+        eq_(f1.data, [1, [4, 2], 2])
+
     def test_remove(self):
         sess = fixture_session()
 
@@ -1270,6 +1293,9 @@ class MutableColumnCopyArrayTest(_MutableListTestBase, fixtures.MappedTest):
     def test_in_place_slice_mutation_str(self):
         """this test is hardcoded to integer, skip strings"""
 
+    def test_insert2(self):
+        """This test does not work with arrays, skip it"""
+
 
 class MutableListWithScalarPickleTest(
     _MutableListTestBase, fixtures.MappedTest