From: Mike Bayer Date: Fri, 17 Mar 2023 12:35:56 +0000 (-0400) Subject: add explicit overload for composite -> callable X-Git-Tag: rel_2_0_7~3^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f898d55be0e6c1ef0ce3105d563d599afe7556bc;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git add explicit overload for composite -> callable Fixed typing issue where :func:`_orm.composite` would not allow an arbitrary callable as the source of the composite class. Fixes: #9502 Change-Id: I5b098b70b2fb7b48f54eaccbb7d5d3d9bdebc781 --- diff --git a/doc/build/changelog/unreleased_20/9502.rst b/doc/build/changelog/unreleased_20/9502.rst new file mode 100644 index 0000000000..4409d62526 --- /dev/null +++ b/doc/build/changelog/unreleased_20/9502.rst @@ -0,0 +1,6 @@ +.. change:: + :tags: bug, typing + :tickets: 9502 + + Fixed typing issue where :func:`_orm.composite` would not allow an + arbitrary callable as the source of the composite class. diff --git a/doc/build/orm/composites.rst b/doc/build/orm/composites.rst index 5737b7e73b..4eaf7024a9 100644 --- a/doc/build/orm/composites.rst +++ b/doc/build/orm/composites.rst @@ -399,7 +399,11 @@ For the purposes of the example, the ``Vertex`` composite is then mapped to a class called ``HasVertex``, which is where the :class:`.Table` containing the four source columns ultimately resides:: + from __future__ import annotations + import dataclasses + from typing import Any + from typing import Tuple from sqlalchemy.orm import composite from sqlalchemy.orm import DeclarativeBase @@ -419,11 +423,11 @@ four source columns ultimately resides:: end: Point @classmethod - def _generate(self, x1, y1, x2, y2): + def _generate(cls, x1: int, y1: int, x2: int, y2: int) -> Vertex: """generate a Vertex from a row""" return Vertex(Point(x1, y1), Point(x2, y2)) - def __composite_values__(self): + def __composite_values__(self) -> Tuple[Any, ...]: """generate a row from a Vertex""" return dataclasses.astuple(self.start) + dataclasses.astuple(self.end) diff --git a/lib/sqlalchemy/orm/_orm_constructors.py b/lib/sqlalchemy/orm/_orm_constructors.py index 64e7937f11..9f9330cead 100644 --- a/lib/sqlalchemy/orm/_orm_constructors.py +++ b/lib/sqlalchemy/orm/_orm_constructors.py @@ -481,6 +481,28 @@ def composite( ... +@overload +def composite( + _class_or_attr: Callable[..., _CC], + *attrs: _CompositeAttrType[Any], + group: Optional[str] = None, + deferred: bool = False, + raiseload: bool = False, + comparator_factory: Optional[Type[Composite.Comparator[_T]]] = None, + active_history: bool = False, + init: Union[_NoArg, bool] = _NoArg.NO_ARG, + repr: Union[_NoArg, bool] = _NoArg.NO_ARG, # noqa: A002 + default: Optional[Any] = _NoArg.NO_ARG, + default_factory: Union[_NoArg, Callable[[], _T]] = _NoArg.NO_ARG, + compare: Union[_NoArg, bool] = _NoArg.NO_ARG, + kw_only: Union[_NoArg, bool] = _NoArg.NO_ARG, + info: Optional[_InfoType] = None, + doc: Optional[str] = None, + **__kw: Any, +) -> Composite[_CC]: + ... + + def composite( _class_or_attr: Union[ None, Type[_CC], Callable[..., _CC], _CompositeAttrType[Any] diff --git a/test/ext/mypy/plain_files/composite.py b/test/ext/mypy/plain_files/composite.py index c69963314e..8ac1f504c2 100644 --- a/test/ext/mypy/plain_files/composite.py +++ b/test/ext/mypy/plain_files/composite.py @@ -33,6 +33,10 @@ class Point: def __ne__(self, other: Any) -> bool: return not self.__eq__(other) + @classmethod + def _generate(cls, x1: int, y1: int) -> "Point": + return Point(x1, y1) + class Vertex(Base): __tablename__ = "vertices" @@ -47,7 +51,7 @@ class Vertex(Base): start = composite(Point, "x1", "y1") # taken from left hand side - end: Mapped[Point] = composite(Point, "x2", "y2") + end: Mapped[Point] = composite(Point._generate, "x2", "y2") v1 = Vertex(start=Point(3, 4), end=Point(5, 6))