Follow-up from #695 (the broader audit triggered by #679).
After fixing the Mosek/COPT/MindOpt probe and _run_file leaks, two solvers remain unaudited because they have no explicit cleanup and rely on the wrapper's __del__:
CPLEX
In Cplex._run_file (linopy/solvers.py:1765):
m = cplex.Cplex()
...
return self._make_result(status, solution, solver_model=m)
m is stored as solver_model and is only ever cleared by Solver.close() setting it to None. The Cplex Python API recommends an explicit m.end() to release the model and the underlying license slot. Whether dropping the reference reliably calls end() via __del__ is library-internal — needs verification with a real CPLEX license.
Xpress
In Xpress._run_file (linopy/solvers.py:2213) and Xpress._build_direct (linopy/solvers.py:2032):
m = xpress.problem()
m.read(...)
...
return self._solve(m, ...)
Same shape — m is the license-bearing object, stored as solver_model, no explicit m.reset() / equivalent.
Suggested approach
- Register cleanup on
_env_stack (e.g. self._env_stack.callback(m.end) for CPLEX) so close() releases the slot deterministically.
- Verify with a real license that calling
end() is idempotent and doesn't break the result accessors that other tests rely on.
Why this wasn't in #695
I don't have CPLEX or Xpress licensed locally and didn't want to ship a "fix" that only works in theory and might break working solver paths in CI. Tagging this so anyone with a CPLEX/Xpress license can pick it up.
Follow-up from #695 (the broader audit triggered by #679).
After fixing the Mosek/COPT/MindOpt probe and
_run_fileleaks, two solvers remain unaudited because they have no explicit cleanup and rely on the wrapper's__del__:CPLEX
In
Cplex._run_file(linopy/solvers.py:1765):mis stored assolver_modeland is only ever cleared bySolver.close()setting it toNone. The Cplex Python API recommends an explicitm.end()to release the model and the underlying license slot. Whether dropping the reference reliably callsend()via__del__is library-internal — needs verification with a real CPLEX license.Xpress
In
Xpress._run_file(linopy/solvers.py:2213) andXpress._build_direct(linopy/solvers.py:2032):Same shape —
mis the license-bearing object, stored assolver_model, no explicitm.reset()/ equivalent.Suggested approach
_env_stack(e.g.self._env_stack.callback(m.end)for CPLEX) soclose()releases the slot deterministically.end()is idempotent and doesn't break the result accessors that other tests rely on.Why this wasn't in #695
I don't have CPLEX or Xpress licensed locally and didn't want to ship a "fix" that only works in theory and might break working solver paths in CI. Tagging this so anyone with a CPLEX/Xpress license can pick it up.