You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Local testing should be stable despite the xpress licence problem.
Note
The following content was generated by AI.
Issue Description
With a local Xpress licence that predates the pip wheel (here: XPAUTH_PATH pointing to a licence valid up to release 9.4, wheel resolves xpress==9.9.1 via the solvers extra), pytest produces ~683 failures, all Xpress. xpress still counts as licensed, so every solver-parametrized test runs it.
Workaround -k "not xpress" is broken as well: it also deselects every test with expression in its name.
Reproducible Example
uv sync --extra dev --extra solvers # xpress 9.9.1, Python 3.13, Linuxexport XPAUTH_PATH=/opt/xpress/xpauth.xpr # licence valid up to 9.4
uv run python -c "import linopy; print(list(linopy.licensed_solvers)); print(linopy.solvers.check_solver_licenses('xpress'))"# ['gurobi', 'highs', 'scip', 'cplex', 'xpress']# {'xpress': LicenseStatus(solver='xpress', ok=True, message=None)}
uv run python -c "import xpress; xpress.init()"# xpress.InterfaceError: Xpress licensing error 21: Your license only authorizes up to release 9.4. Please contact support@fico.com to upgrade it.
With env -u XPAUTH_PATH the wheel's bundled community licence is used and everything passes, so CI (no XPAUTH_PATH) never hits this.
Root cause
Solver.license_status() / licensed_solvers rely on the per-solver _license_probe hook. Gurobi, Knitro, Mosek, COPT, MindOpt, cuPDLPx and cuOpt implement it; Xpress does not, so the no-op default reports it as licensed. On top of that, most test modules (15 files, e.g. test_io.py, test_sos_*.py, test_infeasibility.py) parametrize over available_solvers (importable only) rather than licensed_solvers like test_optimization.py does.
Expected Behavior / proposed solution
A solver that cannot acquire a licence is not in licensed_solvers, and tests that solve only parametrize over licensed_solvers:
# test modules that actually solvefromlinopyimportlicensed_solvers@pytest.mark.parametrize("solver", licensed_solvers)
Stays fail-fast and visible: available_solvers keeps its importable-only semantics (no licence at import), the probe error is exposed via check_solver_licenses(), and test_optimization.py already prints the tested solvers in its header.
Alternatives considered:
Probe the licence in available_solvers: breaks the documented "no licence at import/membership" contract.
Pin xpress<9.5: pins everyone to an old release to fit one local licence; the community licence covers newer releases.
Pytest --exclude-solver option: treats the symptom; still reports an unlicensed solver as licensed to users.
Related: mypy
uv run mypy linopy reports 28 errors in linopy/solvers.py when xpress 9.9.1 is installed. The wheel ships py.typed/__init__.pyi, and the flagged lines are mostly the pre-9.8 fallback branches (chgobjsense, addnames, postsolve, getDual, readbasis, xpress.maximize, ...) plus Optional arrays passed to loadMIQP. CI does not see it because the mypy job installs [dev,spec] without solvers. Related to #533.
mypy output (xpress 9.9.1, mypy 2.3.1)
linopy/solvers.py:2730: error: Module has no attribute "maximize" [attr-defined]
linopy/solvers.py:2755: error: Argument 1 to "enter_context" of "_BaseExitStack" has incompatible type "problem"; expected "AbstractContextManager[Never, bool | None]" [arg-type]
linopy/solvers.py:2854: error: Argument "objqcol1" to "loadMIQP" of "problem" has incompatible type "ndarray[...] | None"; expected "Iterable[var | int | str]" [arg-type]
linopy/solvers.py:2876: error: "problem" has no attribute "loadproblem" [attr-defined]
linopy/solvers.py:2909: error: "problem" has no attribute "chgobjsense"; maybe "chgObjSense"? [attr-defined]
linopy/solvers.py:2922: error: "problem" has no attribute "addnames"; maybe "addNames"? [attr-defined]
linopy/solvers.py:3015: error: Argument 1 to "setControl" of "problem" has incompatible type "dict[str, Any]"; expected "dict[str | int, str | float]" [arg-type]
linopy/solvers.py:3021: error: "problem" has no attribute "setlogfile"; maybe "setLogFile"? [attr-defined]
linopy/solvers.py:3027: error: "problem" has no attribute "readbasis"; maybe "readBasis"? [attr-defined]
linopy/solvers.py:3035: error: "problem" has no attribute "postsolve"; maybe "postSolve"? [attr-defined]
linopy/solvers.py:3042: error: "problem" has no attribute "writebasis"; maybe "writeBasis"? [attr-defined]
linopy/solvers.py:3051: error: "problem" has no attribute "writebinsol"; maybe "writeBinSol"? [attr-defined]
linopy/solvers.py:3080: error: "problem" has no attribute "getDual"; maybe "getDuals"? [attr-defined]
... (28 errors total)
Local testing should be stable despite the xpress licence problem.
Note
The following content was generated by AI.
Issue Description
With a local Xpress licence that predates the pip wheel (here:
XPAUTH_PATHpointing to a licence valid up to release 9.4, wheel resolvesxpress==9.9.1via thesolversextra),pytestproduces ~683 failures, all Xpress.xpressstill counts as licensed, so every solver-parametrized test runs it.Workaround
-k "not xpress"is broken as well: it also deselects every test withexpressionin its name.Reproducible Example
With
env -u XPAUTH_PATHthe wheel's bundled community licence is used and everything passes, so CI (noXPAUTH_PATH) never hits this.Root cause
Solver.license_status()/licensed_solversrely on the per-solver_license_probehook. Gurobi, Knitro, Mosek, COPT, MindOpt, cuPDLPx and cuOpt implement it;Xpressdoes not, so the no-op default reports it as licensed. On top of that, most test modules (15 files, e.g.test_io.py,test_sos_*.py,test_infeasibility.py) parametrize overavailable_solvers(importable only) rather thanlicensed_solversliketest_optimization.pydoes.Expected Behavior / proposed solution
A solver that cannot acquire a licence is not in
licensed_solvers, and tests that solve only parametrize overlicensed_solvers:Stays fail-fast and visible:
available_solverskeeps its importable-only semantics (no licence at import), the probe error is exposed viacheck_solver_licenses(), andtest_optimization.pyalready prints the tested solvers in its header.Alternatives considered:
available_solvers: breaks the documented "no licence at import/membership" contract.xpress<9.5: pins everyone to an old release to fit one local licence; the community licence covers newer releases.--exclude-solveroption: treats the symptom; still reports an unlicensed solver as licensed to users.Related: mypy
uv run mypy linopyreports 28 errors inlinopy/solvers.pywhen xpress 9.9.1 is installed. The wheel shipspy.typed/__init__.pyi, and the flagged lines are mostly the pre-9.8 fallback branches (chgobjsense,addnames,postsolve,getDual,readbasis,xpress.maximize, ...) plusOptionalarrays passed toloadMIQP. CI does not see it because the mypy job installs[dev,spec]without solvers. Related to #533.mypy output (xpress 9.9.1, mypy 2.3.1)
Installed Versions
linopy master /
spec-whole-model(0.9.1.post1.dev89), xpress 9.9.1, mypy 2.3.1, Python 3.13.11, Ubuntu 24.04.