From 54a1dbbf1f67e39bbc5a9a2224c3ecac141eaaaa Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 12 Oct 2026 20:54:00 +0800 Subject: [PATCH 1/4] Remove unused modulo parameter from __pow__ Expr.__pow__ and GenExpr.__pow__ declared a third parameter modulo that the method bodies never used, so pow(x, y, mod) silently ignored the modulus and returned an Expr. Drop the parameter from both Cython definitions and sync the four affected mod: Incomplete = ... stubs in scip.pyi. pow(x, y, mod) now raises TypeError, matching the Python convention that three-arg pow is only meaningful for integers. --- src/pyscipopt/expr.pxi | 4 ++-- src/pyscipopt/scip.pyi | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/pyscipopt/expr.pxi b/src/pyscipopt/expr.pxi index 73743a969..1639e399e 100644 --- a/src/pyscipopt/expr.pxi +++ b/src/pyscipopt/expr.pxi @@ -404,7 +404,7 @@ cdef class Expr(ExprLike): return NotImplemented return super().__rtruediv__(other) - def __pow__(self, other, modulo): + def __pow__(self, other): if float(other).is_integer() and other >= 0: exp = int(other) else: # need to transform to GenExpr @@ -679,7 +679,7 @@ cdef class GenExpr(ExprLike): # self.children.append(right) # return self - def __pow__(self, other, modulo): + def __pow__(self, other): expo = buildGenExprObj(other) if Py_TYPE(expo) is not Constant: raise NotImplementedError("exponents must be numbers") diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index 316b13e9f..c013d5435 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -254,7 +254,7 @@ class Constant(GenExpr): def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ... def __neg__(self, /) -> Constant: ... def __pow__( # type: ignore[override] - self, other: float | Constant, mod: Incomplete = ..., / + self, other: float | Constant, / ) -> Constant: ... @disjoint_base @@ -419,9 +419,9 @@ class Expr(ExprLike): # PowExpr can be wrong. We aim to make the stubs useful in common cases # even though the type system cannot fully express the real types. @overload - def __pow__(self, other: int, mod: Incomplete = ..., /) -> Expr: ... + def __pow__(self, other: int, /) -> Expr: ... @overload - def __pow__(self, other: float, mod: Incomplete = ..., /) -> PowExpr: ... + def __pow__(self, other: float, /) -> PowExpr: ... def __rpow__(self, other: float, /) -> ExpExpr: ... def __getitem__(self, index: Incomplete, /) -> Incomplete: ... def __iter__(self) -> Incomplete: ... @@ -467,7 +467,7 @@ class GenExpr(ExprLike): @overload def __truediv__(self, other: np.ndarray | MatrixExpr, /) -> MatrixExpr: ... def __rtruediv__(self, other: float, /) -> ProdExpr: ... - def __pow__(self, other: float | Constant, mod: Incomplete = ..., /) -> PowExpr: ... + def __pow__(self, other: float | Constant, /) -> PowExpr: ... def __rpow__(self, other: float, mod: Incomplete = ..., /) -> ExpExpr: ... @disjoint_base From be416db12db9122cdf7d8aa8aa8833af4557643a Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 12 Oct 2026 20:54:07 +0800 Subject: [PATCH 2/4] Test that pow(x, y, mod) raises TypeError --- tests/test_expr.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_expr.py b/tests/test_expr.py index c5beb2ecb..0efba4f01 100644 --- a/tests/test_expr.py +++ b/tests/test_expr.py @@ -203,6 +203,23 @@ def test_rpow_constant_base(model): c = (-2)**x +def test_pow_three_arg_raises(model): + m, x, y, z = model + + # Expr path (integer exponent; falls through to GenExpr only for non-int) + with pytest.raises(TypeError): + pow(x, 2, 5) + + # GenExpr path + with pytest.raises(TypeError): + pow(x**1.5, 2, 5) + + # Two-argument pow still works on both paths + assert repr(x**2) == repr(pow(x, 2)) + assert isinstance(x**1.5, GenExpr) + assert isinstance(pow(x, 1.5), GenExpr) + + def test_getVal_with_GenExpr(): m = Model() x = m.addVar(lb=1, ub=1, name="x") From 0b9ba64cee5e4e6436344f8e6cc3a2ff0642fa20 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 12 Oct 2026 20:54:19 +0800 Subject: [PATCH 3/4] Changelog: note pow modulo behavior change --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c19ccfa4f..507593f82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ ### Added ### Fixed ### Changed +- `pow(x, y, modulo)` now raises `TypeError` instead of silently ignoring the modulo argument ### Removed ## 6.3.0 - 2026.10.11 From a709145c94f7c7e9bfb876472b7946e232354864 Mon Sep 17 00:00:00 2001 From: 40% Date: Mon, 12 Oct 2026 21:49:49 +0800 Subject: [PATCH 4/4] Collapse one-line Constant.__pow__ stub to a single line --- src/pyscipopt/scip.pyi | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi index c013d5435..1f6e3b011 100644 --- a/src/pyscipopt/scip.pyi +++ b/src/pyscipopt/scip.pyi @@ -253,9 +253,7 @@ class Constant(GenExpr): number: Incomplete def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ... def __neg__(self, /) -> Constant: ... - def __pow__( # type: ignore[override] - self, other: float | Constant, / - ) -> Constant: ... + def __pow__(self, other: float | Constant, /) -> Constant: ... # type: ignore[override] @disjoint_base class Constraint: