]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
use read-only Mapping for values dictionary type
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 26 Feb 2023 20:34:57 +0000 (15:34 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 27 Feb 2023 01:09:07 +0000 (20:09 -0500)
Improved typing for the mapping passed to :meth:`.UpdateBase.values` to be
more open-ended about collection type, by indicating read-only ``Mapping``
instead of writeable ``Dict``, the latter of which would error out under
typing tools on too limited of a key type.

Fixes: #9376
Change-Id: Ib7fdbba05ca7e1082409e1b5616e6a010262f032

doc/build/changelog/unreleased_20/9376.rst [new file with mode: 0644]
lib/sqlalchemy/sql/_typing.py
lib/sqlalchemy/sql/dml.py
test/ext/mypy/plain_files/dml.py [new file with mode: 0644]

diff --git a/doc/build/changelog/unreleased_20/9376.rst b/doc/build/changelog/unreleased_20/9376.rst
new file mode 100644 (file)
index 0000000..6d63d09
--- /dev/null
@@ -0,0 +1,8 @@
+.. change::
+    :tags: bug, typing
+    :tickets: 9376
+
+    Improved typing for the mapping passed to :meth:`.UpdateBase.values` to be
+    more open-ended about collection type, by indicating read-only ``Mapping``
+    instead of writeable ``Dict`` which would error out on too limited of a key
+    type.
index 6bf9a5a1f4bcd508866998de08a116a980ee219a..a828d6a0fb8f6986e0547866ce701e7c164c54eb 100644 (file)
@@ -11,6 +11,7 @@ import operator
 from typing import Any
 from typing import Callable
 from typing import Dict
+from typing import Mapping
 from typing import Set
 from typing import Tuple
 from typing import Type
@@ -238,6 +239,9 @@ the DMLColumnRole to be able to accommodate.
 
 """
 
+_DMLKey = TypeVar("_DMLKey", bound=_DMLColumnArgument)
+_DMLColumnKeyMapping = Mapping[_DMLKey, Any]
+
 
 _DDLColumnArgument = Union[str, "Column[Any]", roles.DDLConstraintColumnRole]
 """DDL column.
index 9042fdff7bcecda4d3a4bd8d3f132b27e42887d7..dbbf09f1b930caef5eb7da878c4fc28ae20717ec 100644 (file)
@@ -72,6 +72,7 @@ if TYPE_CHECKING:
     from ._typing import _ColumnExpressionArgument
     from ._typing import _ColumnsClauseArgument
     from ._typing import _DMLColumnArgument
+    from ._typing import _DMLColumnKeyMapping
     from ._typing import _DMLTableArgument
     from ._typing import _T0  # noqa
     from ._typing import _T1  # noqa
@@ -944,7 +945,7 @@ class ValuesBase(UpdateBase):
     def values(
         self,
         *args: Union[
-            Dict[_DMLColumnArgument, Any],
+            _DMLColumnKeyMapping[Any],
             Sequence[Any],
         ],
         **kwargs: Any,
diff --git a/test/ext/mypy/plain_files/dml.py b/test/ext/mypy/plain_files/dml.py
new file mode 100644 (file)
index 0000000..d2ffbf1
--- /dev/null
@@ -0,0 +1,35 @@
+from __future__ import annotations
+
+from typing import Any
+from typing import Dict
+
+from sqlalchemy import Column
+from sqlalchemy import insert
+from sqlalchemy.orm import DeclarativeBase
+from sqlalchemy.orm import Mapped
+from sqlalchemy.orm import mapped_column
+
+
+class Base(DeclarativeBase):
+    pass
+
+
+class User(Base):
+    __tablename__ = "user"
+
+    id: Mapped[int] = mapped_column(primary_key=True)
+    name: Mapped[str]
+    data: Mapped[str]
+
+
+# test #9376
+d1: dict[str, Any] = {}
+stmt1 = insert(User).values(d1)
+
+
+d2: Dict[str, Any] = {}
+stmt2 = insert(User).values(d2)
+
+
+d3: Dict[Column[str], Any] = {}
+stmt3 = insert(User).values(d3)