From: Aramís Segovia Date: Sat, 10 May 2025 16:36:46 +0000 (-0400) Subject: Support `matmul` (@) as an optional operator. X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a5cad30a4d3d9ecb4a4a31a4ce600128bab37524;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Support `matmul` (@) as an optional operator. Fixes: #12479 --- diff --git a/doc/build/changelog/unreleased_21/12479.rst b/doc/build/changelog/unreleased_21/12479.rst new file mode 100644 index 0000000000..4cced479b1 --- /dev/null +++ b/doc/build/changelog/unreleased_21/12479.rst @@ -0,0 +1,6 @@ +.. change:: + :tags: core, feature, sql + :tickets: 12479 + + The Core operator system now includes the `matmul` operator, i.e. the + @ operator in Python as an optional operator. diff --git a/lib/sqlalchemy/sql/default_comparator.py b/lib/sqlalchemy/sql/default_comparator.py index c1305be994..eba769f892 100644 --- a/lib/sqlalchemy/sql/default_comparator.py +++ b/lib/sqlalchemy/sql/default_comparator.py @@ -558,6 +558,7 @@ operator_lookup: Dict[ "getitem": (_getitem_impl, util.EMPTY_DICT), "lshift": (_unsupported_impl, util.EMPTY_DICT), "rshift": (_unsupported_impl, util.EMPTY_DICT), + "matmul": (_unsupported_impl, util.EMPTY_DICT), "contains": (_unsupported_impl, util.EMPTY_DICT), "regexp_match_op": (_regexp_match_impl, util.EMPTY_DICT), "not_regexp_match_op": (_regexp_match_impl, util.EMPTY_DICT), diff --git a/lib/sqlalchemy/sql/elements.py b/lib/sqlalchemy/sql/elements.py index 42dfe61106..75bf72f033 100644 --- a/lib/sqlalchemy/sql/elements.py +++ b/lib/sqlalchemy/sql/elements.py @@ -924,6 +924,8 @@ class SQLCoreOperations(Generic[_T_co], ColumnOperators, TypingOnly): def __rshift__(self, other: Any) -> ColumnElement[Any]: ... + def __matmul__(self, other: Any) -> ColumnElement[Any]: ... + @overload def concat(self: _SQO[str], other: Any) -> ColumnElement[str]: ... diff --git a/lib/sqlalchemy/sql/operators.py b/lib/sqlalchemy/sql/operators.py index 635e5712ad..ed0582d309 100644 --- a/lib/sqlalchemy/sql/operators.py +++ b/lib/sqlalchemy/sql/operators.py @@ -25,6 +25,7 @@ from operator import inv as _uncast_inv from operator import le as _uncast_le from operator import lshift as _uncast_lshift from operator import lt as _uncast_lt +from operator import matmul as _uncast_matmul from operator import mod as _uncast_mod from operator import mul as _uncast_mul from operator import ne as _uncast_ne @@ -110,6 +111,7 @@ inv = cast(OperatorType, _uncast_inv) le = cast(OperatorType, _uncast_le) lshift = cast(OperatorType, _uncast_lshift) lt = cast(OperatorType, _uncast_lt) +matmul = cast(OperatorType, _uncast_matmul) mod = cast(OperatorType, _uncast_mod) mul = cast(OperatorType, _uncast_mul) ne = cast(OperatorType, _uncast_ne) @@ -678,6 +680,15 @@ class ColumnOperators(Operators): """ return self.operate(rshift, other) + def __matmul__(self, other: Any) -> ColumnOperators: + """Implement the @ operator. + + Not used by SQLAlchemy core, this is provided + for custom operator systems which want to use + @ as an extension point. + """ + return self.operate(matmul, other) + def concat(self, other: Any) -> ColumnOperators: """Implement the 'concat' operator. diff --git a/test/sql/test_operators.py b/test/sql/test_operators.py index 099301707f..753a2e3c1b 100644 --- a/test/sql/test_operators.py +++ b/test/sql/test_operators.py @@ -977,6 +977,16 @@ class ExtensionOperatorTest(fixtures.TestBase, testing.AssertsCompiledSQL): self.assert_compile(Column("x", MyType()) >> 5, "x -> :x_1") + def test_matmul(self): + class MyType(UserDefinedType): + cache_ok = True + + class comparator_factory(UserDefinedType.Comparator): + def __matmul__(self, other): + return self.op("->")(other) + + self.assert_compile(Column("x", MyType()) @ 5, "x -> :x_1") + class JSONIndexOpTest(fixtures.TestBase, testing.AssertsCompiledSQL): def setup_test(self):