Skip to content

Conversation

@Joao-Dionisio
Copy link
Member

@jonathanberthias how do you feel about this? I'm a bit more concerned since, now that the script would be running automatically, we should be more comfortable that it's actually correct.

Added isObjIntegral() to test that the pipelines do what they're supposed to.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds automation for regenerating type stubs in the CI/CD pipeline and wraps the isObjIntegral() SCIP function with corresponding tests. The stub generation script will automatically regenerate type stubs during PR checks if they are out of date, and commit the updated stubs back to the PR.

Changes:

  • Added isObjIntegral() wrapper method to query whether the objective function is integral
  • Added test cases for isObjIntegral() to verify the method works correctly
  • Modified the stubs workflow to automatically regenerate and commit type stubs when they're out of date during PR checks

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/pyscipopt/scip.pxd Added C function declaration for SCIPisObjIntegral
src/pyscipopt/scip.pxi Implemented Python wrapper method isObjIntegral() with documentation
tests/test_model.py Added test function to verify isObjIntegral() behavior in two scenarios
.github/workflows/stubs.yml Added automated stub regeneration, commit, and PR comment steps
CHANGELOG.md Documented the new features in the unreleased section

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@Joao-Dionisio Joao-Dionisio marked this pull request as draft January 14, 2026 15:55
@Joao-Dionisio Joao-Dionisio marked this pull request as ready for review January 14, 2026 16:10
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stub version is a regression from before, we lost all the names of parameters from #1145. We can try to automate the stubs for newly added functions, but we shouldn't overwrite what's already there

Copy link
Member Author

@Joao-Dionisio Joao-Dionisio Jan 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yeah, this is still being adapted. But on the overall approach, assuming things work out, how do you feel? Regarding the script being called automatically by the pipelines whenever the stub tests fails

@codecov
Copy link

codecov bot commented Jan 14, 2026

Codecov Report

❌ Patch coverage is 50.00000% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 55.12%. Comparing base (5ceac82) to head (75fdde8).
⚠️ Report is 6 commits behind head on master.

Files with missing lines Patch % Lines
src/pyscipopt/scip.pxi 50.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #1152      +/-   ##
==========================================
+ Coverage   55.07%   55.12%   +0.05%     
==========================================
  Files          24       24              
  Lines        5420     5440      +20     
==========================================
+ Hits         2985     2999      +14     
- Misses       2435     2441       +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@Joao-Dionisio Joao-Dionisio marked this pull request as draft January 14, 2026 17:04
@Joao-Dionisio Joao-Dionisio marked this pull request as ready for review January 14, 2026 17:07
@github-actions
Copy link

🤖 Type stubs automatically regenerated

The type stubs were out of date and have been automatically regenerated. A new commit has been pushed to this PR.

Changes summary
 src/pyscipopt/scip.pyi | 90 +++++++++++++++++++++++++++++++++-----------------
 1 file changed, 60 insertions(+), 30 deletions(-)
Detailed diff (first 100 lines)
diff --git a/src/pyscipopt/scip.pyi b/src/pyscipopt/scip.pyi
index e06e718..db52edc 100644
--- a/src/pyscipopt/scip.pyi
+++ b/src/pyscipopt/scip.pyi
@@ -156,7 +156,7 @@ class Conshdlr:
 @disjoint_base
 class Constant(GenExpr):
     number: Incomplete
-    def __init__(self, number: Incomplete) -> None: ...
+    def __init__(self, *args: Incomplete, **kwargs: Incomplete) -> None: ...
 
 @disjoint_base
 class Constraint:
@@ -259,7 +259,7 @@ class Expr:
     def __ne__(self, other: object) -> bool: ...
     def __neg__(self) -> Incomplete: ...
     def __next__(self) -> Incomplete: ...
-    def __pow__(self, other: Incomplete, mod: Incomplete = ...) -> Incomplete: ...
+    def __pow__(self, other: Incomplete, modulo: Incomplete = ...) -> Incomplete: ...
     def __radd__(self, other: Incomplete) -> Incomplete: ...
     def __rmul__(self, other: Incomplete) -> Incomplete: ...
     def __rpow__(self, other: Incomplete) -> Incomplete: ...
@@ -300,7 +300,7 @@ class GenExpr:
     def __mul__(self, other: Incomplete) -> Incomplete: ...
     def __ne__(self, other: object) -> bool: ...
     def __neg__(self) -> Incomplete: ...
-    def __pow__(self, other: Incomplete, mod: Incomplete = ...) -> Incomplete: ...
+    def __pow__(self, other: Incomplete, modulo: Incomplete = ...) -> Incomplete: ...
     def __radd__(self, other: Incomplete) -> Incomplete: ...
     def __rmul__(self, other: Incomplete) -> Incomplete: ...
     def __rpow__(self, other: Incomplete) -> Incomplete: ...
@@ -406,7 +406,7 @@ class MatrixExpr(numpy.ndarray):
     def __le__(self, other: object) -> bool: ...  # type: ignore[override]
     def __matmul__(self, other: Incomplete) -> Incomplete: ...
     def __mul__(self, other: Incomplete) -> Incomplete: ...
-    def __pow__(self, other: Incomplete, mod: Incomplete = ...) -> Incomplete: ...
+    def __pow__(self, other: Incomplete) -> Incomplete: ...  # type: ignore[override]
     def __radd__(self, other: Incomplete) -> Incomplete: ...
     def __rmul__(self, other: Incomplete) -> Incomplete: ...
     def __rsub__(self, other: Incomplete) -> Incomplete: ...
@@ -467,13 +467,13 @@ class Model:
     def addConsSOS2(self, vars: Incomplete, weights: Incomplete = ..., name: Incomplete = ..., initial: Incomplete = ..., separate: Incomplete = ..., enforce: Incomplete = ..., check: Incomplete = ..., propagate: Incomplete = ..., local: Incomplete = ..., dynamic: Incomplete = ..., removable: Incomplete = ..., stickingatnode: Incomplete = ...) -> Incomplete: ...
     def addConsXor(self, vars: Incomplete, rhsvar: Incomplete, name: Incomplete = ..., initial: Incomplete = ..., separate: Incomplete = ..., enforce: Incomplete = ..., check: Incomplete = ..., propagate: Incomplete = ..., local: Incomplete = ..., modifiable: Incomplete = ..., dynamic: Incomplete = ..., removable: Incomplete = ..., stickingatnode: Incomplete = ...) -> Incomplete: ...
     def addConss(self, conss: Incomplete, name: Incomplete = ..., initial: Incomplete = ..., separate: Incomplete = ..., enforce: Incomplete = ..., check: Incomplete = ..., propagate: Incomplete = ..., local: Incomplete = ..., modifiable: Incomplete = ..., dynamic: Incomplete = ..., removable: Incomplete = ..., stickingatnode: Incomplete = ...) -> Incomplete: ...
-    def addCut(self, forcecut: Incomplete = ...) -> Incomplete: ...
+    def addCut(self, cut: Incomplete, forcecut: Incomplete = ...) -> Incomplete: ...
     def addExprNonlinear(self, cons: Incomplete, expr: Incomplete, coef: Incomplete) -> Incomplete: ...
     def addMatrixCons(self, cons: Incomplete, name: Incomplete = ..., initial: Incomplete = ..., separate: Incomplete = ..., enforce: Incomplete = ..., check: Incomplete = ..., propagate: Incomplete = ..., local: Incomplete = ..., modifiable: Incomplete = ..., dynamic: Incomplete = ..., removable: Incomplete = ..., stickingatnode: Incomplete = ...) -> Incomplete: ...
     def addMatrixConsIndicator(self, cons: Incomplete, binvar: Incomplete = ..., activeone: Incomplete = ..., name: Incomplete = ..., initial: Incomplete = ..., separate: Incomplete = ..., enforce: Incomplete = ..., check: Incomplete = ..., propagate: Incomplete = ..., local: Incomplete = ..., dynamic: Incomplete = ..., removable: Incomplete = ..., stickingatnode: Incomplete = ...) -> Incomplete: ...
     def addMatrixVar(self, shape: Incomplete, name: Incomplete = ..., vtype: Incomplete = ..., lb: Incomplete = ..., ub: Incomplete = ..., obj: Incomplete = ..., pricedVar: Incomplete = ..., pricedVarScore: Incomplete = ...) -> Incomplete: ...
     def addObjoffset(self, offset: Incomplete, solutions: Incomplete = ...) -> Incomplete: ...
-    def addPoolCut(self) -> Incomplete: ...
+    def addPoolCut(self, row: Incomplete) -> Incomplete: ...
     def addPyCons(self, cons: Incomplete) -> Incomplete: ...
     def addRowDive(self, row: Incomplete) -> Incomplete: ...
     def addRowExact(self, rowexact: Incomplete) -> Incomplete: ...
@@ -483,7 +483,7 @@ class Model:
     def addVarLocksType(self, var: Incomplete, locktype: Incomplete, nlocksdown: Incomplete, nlocksup: Incomplete) -> Incomplete: ...
     def addVarSOS1(self, cons: Incomplete, var: Incomplete, weight: Incomplete) -> Incomplete: ...
     def addVarSOS2(self, cons: Incomplete, var: Incomplete, weight: Incomplete) -> Incomplete: ...
-    def addVarToRow(self, value: Incomplete) -> Incomplete: ...
+    def addVarToRow(self, row: Incomplete, var: Incomplete, value: Incomplete) -> Incomplete: ...
     def allColsInLP(self) -> Incomplete: ...
     def allowNegSlackExact(self) -> Incomplete: ...
     def appendVarSOS1(self, cons: Incomplete, var: Incomplete) -> Incomplete: ...
@@ -494,7 +494,7 @@ class Model:
     def branchLPExact(self) -> Incomplete: ...
     def branchVar(self, variable: Incomplete) -> Incomplete: ...
     def branchVarVal(self, variable: Incomplete, value: Incomplete) -> Incomplete: ...
-    def cacheRowExtensions(self) -> Incomplete: ...
+    def cacheRowExtensions(self, row: Incomplete) -> Incomplete: ...
     def calcChildEstimate(self, variable: Incomplete, targetvalue: Incomplete) -> Incomplete: ...
     def calcNodeselPriority(self, variable: Incomplete, branchdir: Incomplete, targetvalue: Incomplete) -> Incomplete: ...
     def catchEvent(self, eventtype: Incomplete, eventhdlr: Incomplete) -> Incomplete: ...
@@ -562,7 +562,7 @@ class Model:
     def feastol(self) -> Incomplete: ...
     def fixVar(self, var: Incomplete, val: Incomplete) -> Incomplete: ...
     def fixVarProbing(self, var: Incomplete, fixedval: Incomplete) -> Incomplete: ...
-    def flushRowExtensions(self) -> Incomplete: ...
+    def flushRowExtensions(self, row: Incomplete) -> Incomplete: ...
     def frac(self, value: Incomplete) -> Incomplete: ...
     def freeBendersSubproblems(self) -> Incomplete: ...
     def freeProb(self) -> Incomplete: ...
@@ -593,8 +593,8 @@ class Model:
     def getConsVars(self, constraint: Incomplete) -> Incomplete: ...
     def getConss(self, transformed: Incomplete = ...) -> Incomplete: ...
     def getCurrentNode(self) -> Incomplete: ...
-    def getCutEfficacy(self, sol: Incomplete = ...) -> Incomplete: ...
-    def getCutLPSolCutoffDistance(self) -> Incomplete: ...
+    def getCutEfficacy(self, cut: Incomplete, sol: Incomplete = ...) -> Incomplete: ...
+    def getCutLPSolCutoffDistance(self, cut: Incomplete, sol: Incomplete) -> Incomplete: ...
     def getCutoffbound(self) -> Incomplete: ...
     def getDepth(self) -> Incomplete: ...
     def getDualMultiplier(self, cons: Incomplete) -> Incomplete: ...
@@ -743,7 +743,7 @@ class Model:
     def initBendersDefault(self, subproblems: Incomplete) -> Incomplete: ...
     def interruptSolve(self) -> Incomplete: ...
     def isAndConsSorted(self, and_cons: Incomplete) -> Incomplete: ...
-    def isCutEfficacious(self, sol: Incomplete = ...) -> Incomplete: ...
+    def isCutEfficacious(self, cut: Incomplete, sol: Incomplete = ...) -> Incomplete: ...

@Joao-Dionisio
Copy link
Member Author

Heck yeah!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants