]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Allow None as default values for MutableDict
authorNils Philippsen <nils@tiptoe.de>
Mon, 27 Feb 2023 18:27:50 +0000 (19:27 +0100)
committerNils Philippsen <nils@tiptoe.de>
Tue, 28 Feb 2023 21:02:54 +0000 (22:02 +0100)
Previously, e.g. `MutableDict.pop(..., None)` wouldn’t return `None` if
the key doesn’t exist.

Fixes: #9380
Signed-off-by: Nils Philippsen <nils@tiptoe.de>
lib/sqlalchemy/ext/mutable.py
test/ext/test_mutable.py

index 07f357bd6be830bb6d8d742cbb5777012bea2cec..6c87749c5f4337fd1044f05c39c15896405a7ac5 100644 (file)
@@ -389,10 +389,13 @@ from ..orm.context import QueryContext
 from ..orm.decl_api import DeclarativeAttributeIntercept
 from ..orm.state import InstanceState
 from ..orm.unitofwork import UOWTransaction
+from ..sql.base import _NoArg
+from ..sql.base import NO_ARG
 from ..sql.base import SchemaEventTarget
 from ..sql.schema import Column
 from ..sql.type_api import TypeEngine
 from ..util import memoized_property
+from ..util.typing import Literal
 from ..util.typing import SupportsIndex
 from ..util.typing import TypeGuard
 
@@ -781,8 +784,10 @@ 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
+    def _exists(
+        self, value: _T | None | Literal[_NoArg.NO_ARG]
+    ) -> TypeGuard[_T]:
+        return value is not NO_ARG
 
     def _is_none(self, value: _T | None) -> TypeGuard[None]:
         return value is None
@@ -795,7 +800,9 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
     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 | Literal[_NoArg.NO_ARG] = NO_ARG
+    ) -> _VT | None:
         if self._exists(value):
             result = super().setdefault(key, value)
         else:
@@ -820,7 +827,11 @@ class MutableDict(Mutable, Dict[_KT, _VT]):
     def pop(self, __key: _KT, __default: _VT | _T) -> _VT | _T:
         ...
 
-    def pop(self, __key: _KT, __default: _VT | _T | None = None) -> _VT | _T:
+    def pop(
+        self,
+        __key: _KT,
+        __default: _VT | _T | None | Literal[_NoArg.NO_ARG] = NO_ARG,
+    ) -> _VT | _T:
         if self._exists(__default):
             result = super().pop(__key, __default)
         else:
index 50dc22e3514d5307807fdc39c5038003679a9767..9a9ebc92c8e3a215f61634f47ecd664dad32f3bd 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()