summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/build/changelog/unreleased_20/9376.rst8
-rw-r--r--lib/sqlalchemy/sql/_typing.py4
-rw-r--r--lib/sqlalchemy/sql/dml.py3
-rw-r--r--test/ext/mypy/plain_files/dml.py35
4 files changed, 49 insertions, 1 deletions
diff --git a/doc/build/changelog/unreleased_20/9376.rst b/doc/build/changelog/unreleased_20/9376.rst
new file mode 100644
index 000000000..6d63d09d4
--- /dev/null
+++ b/doc/build/changelog/unreleased_20/9376.rst
@@ -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.
diff --git a/lib/sqlalchemy/sql/_typing.py b/lib/sqlalchemy/sql/_typing.py
index 6bf9a5a1f..a828d6a0f 100644
--- a/lib/sqlalchemy/sql/_typing.py
+++ b/lib/sqlalchemy/sql/_typing.py
@@ -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.
diff --git a/lib/sqlalchemy/sql/dml.py b/lib/sqlalchemy/sql/dml.py
index 9042fdff7..dbbf09f1b 100644
--- a/lib/sqlalchemy/sql/dml.py
+++ b/lib/sqlalchemy/sql/dml.py
@@ -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
index 000000000..d2ffbf1e1
--- /dev/null
+++ b/test/ext/mypy/plain_files/dml.py
@@ -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)