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 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..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, mod: Incomplete = ..., / - ) -> Constant: ... + def __pow__(self, other: float | Constant, /) -> Constant: ... # type: ignore[override] @disjoint_base class Constraint: @@ -419,9 +417,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 +465,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 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")