]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
restore old *args approach for MutableDict.pop()
authorNils Philippsen <nils@tiptoe.de>
Tue, 28 Feb 2023 21:04:54 +0000 (16:04 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sat, 4 Mar 2023 16:09:57 +0000 (11:09 -0500)
The typing change in ba0e508141206efc55cdab91df21c1
changed the semantics of pop() and possibly setdefault() in order to
try working at runtime with a two-argument signature.  however
the implementation for this in cpython likely uses a strict `*args`
approach where the lack of the second parameter is explicit, rather
than matching to a constant.  Restore the old implementation inside
of a "not TYPE_CHECKING" block while keeping the type annotated forms
intact for typing only.

Fixed regression caused by typing added to ``sqlalchemy.ext.mutable`` for
:ticket:`8667`, where the semantics of the ``.pop()`` method changed such
that the method was non-working.  Pull request courtesy Nils Philippsen.

Fixes: #9380
Closes: #9381
Co-authored-by: Mike Bayer <mike_mp@zzzcomputing.com>
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/9381
Pull-request-sha: fd903ce1b949d2af26ceb6c2159ad84aab007f3d

Change-Id: I213e52f51a795801aacf05307e38cc8c89b54e12

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

diff --git a/doc/build/changelog/unreleased_20/9380.rst b/doc/build/changelog/unreleased_20/9380.rst
new file mode 100644 (file)
index 0000000..6ac454e
--- /dev/null
@@ -0,0 +1,7 @@
+.. change::
+    :tags: bug, ext, regression
+    :tickets: 9380
+
+    Fixed regression caused by typing added to ``sqlalchemy.ext.mutable`` for
+    :ticket:`8667`, where the semantics of the ``.pop()`` method changed such
+    that the method was non-working. Pull request courtesy Nils Philippsen.
index 07f357bd6be830bb6d8d742cbb5777012bea2cec..940d62b0aab62f486d1b65b3d4a5a0c63edf4d90 100644 (file)
@@ -369,6 +369,7 @@ from typing import Optional
 from typing import overload
 from typing import Set
 from typing import Tuple
+from typing import TYPE_CHECKING
 from typing import TypeVar
 from typing import Union
 import weakref
@@ -781,27 +782,25 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
         super().__setitem__(key, value)
         self.changed()
 
-    def _exists(self, value: _T | None) -> TypeGuard[_T]:
-        return value is not None
+    if TYPE_CHECKING:
 
-    def _is_none(self, value: _T | None) -> TypeGuard[None]:
-        return value is None
+        @overload
+        def setdefault(self, key: _KT) -> _VT | None:
+            ...
 
-    @overload
-    def setdefault(self, key: _KT) -> _VT | None:
-        ...
+        @overload
+        def setdefault(self, key: _KT, value: _VT) -> _VT:
+            ...
 
-    @overload
-    def setdefault(self, key: _KT, value: _VT) -> _VT:
-        ...
+        def setdefault(self, key: _KT, value: _VT | None = None) -> _VT | None:
+            ...
 
-    def setdefault(self, key: _KT, value: _VT | None = None) -> _VT | None:
-        if self._exists(value):
-            result = super().setdefault(key, value)
-        else:
-            result = super().setdefault(key)  # type: ignore[call-arg]
-        self.changed()
-        return result
+    else:
+
+        def setdefault(self, *arg):  # noqa: F811
+            result = super().setdefault(*arg)
+            self.changed()
+            return result
 
     def __delitem__(self, key: _KT) -> None:
         """Detect dictionary del events and emit change events."""
@@ -812,21 +811,27 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
         super().update(*a, **kw)
         self.changed()
 
-    @overload
-    def pop(self, __key: _KT) -> _VT:
-        ...
+    if TYPE_CHECKING:
 
-    @overload
-    def pop(self, __key: _KT, __default: _VT | _T) -> _VT | _T:
-        ...
+        @overload
+        def pop(self, __key: _KT) -> _VT:
+            ...
 
-    def pop(self, __key: _KT, __default: _VT | _T | None = None) -> _VT | _T:
-        if self._exists(__default):
-            result = super().pop(__key, __default)
-        else:
-            result = super().pop(__key)
-        self.changed()
-        return result
+        @overload
+        def pop(self, __key: _KT, __default: _VT | _T) -> _VT | _T:
+            ...
+
+        def pop(
+            self, __key: _KT, __default: _VT | _T | None = None
+        ) -> _VT | _T:
+            ...
+
+    else:
+
+        def pop(self, *arg):  # noqa: F811
+            result = super().pop(*arg)
+            self.changed()
+            return result
 
     def popitem(self) -> Tuple[_KT, _VT]:
         result = super().popitem()
index 50dc22e3514d5307807fdc39c5038003679a9767..290518dd6792de3705d59dae0cb63b33b6236235 100644 (file)
@@ -349,6 +349,19 @@ class _MutableDictTestBase(_MutableDictTestFixture):
 
         eq_(f1.data, {"c": "d"})
 
+    def test_pop_default_none(self):
+        sess = fixture_session()
+
+        f1 = Foo(data={"a": "b", "c": "d"})
+        sess.add(f1)
+        sess.commit()
+
+        eq_(f1.data.pop("a", None), "b")
+        eq_(f1.data.pop("a", None), None)
+        sess.commit()
+
+        eq_(f1.data, {"c": "d"})
+
     def test_popitem(self):
         sess = fixture_session()
 
@@ -386,6 +399,10 @@ class _MutableDictTestBase(_MutableDictTestFixture):
 
         eq_(f1.data, {"a": "b", "c": "d"})
 
+        eq_(f1.data.setdefault("w", None), None)
+        sess.commit()
+        eq_(f1.data, {"a": "b", "c": "d", "w": None})
+
     def test_replace(self):
         sess = fixture_session()
         f1 = Foo(data={"a": "b"})