Xpress missing _license_probe: licensed_solvers includes xpress with an out-of-date licence, breaking local tests
I maintainer di solito rispondono entro 1 giorno
@FabianHofmann ci sta già lavorando.
Dal 28/9/2026.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 58/100
- Tipo di issue
- Bug
- Chiarezza
- Abbastanza chiara
- Stato di attività
- Attiva
- Stack tecnologico
- python
- Ambito
- backend, testing-qa
Direzione di ricerca
Start in linopy/solvers.py with Solver.license_status(), licensed_solvers, and the Xpress solver implementation; reproduce the stale-license case with XPAUTH_PATH and check_solver_licenses(). Review test_optimization.py alongside solving modules such as test_io.py, test_sos_*.py, and test_infeasibility.py. Done means the probe exposes the invalid licence and solving tests use licensed_solvers without breaking available_solvers semantics.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
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, Linux
export 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 [email protected] 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:
class Xpress(Solver[None]):
@classmethod
def _license_probe(cls) -> None:
xpress.init()
# test modules that actually solve
from linopy import licensed_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-solveroption: 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)
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.
- Lingua principale
- Python
- Stelle
- 259
- Fork
- 89
- Merge medio
- 1g 18h
- PR unite (30g)
- 26
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Ha un modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di PyPSA/linopy
-
bug solver interface
Difficoltà 2/5 1-3 ore Idoneità per principianti 84/100
I maintainer di solito rispondono entro 1 giorno
-
refactor sparse
Difficoltà 5/5 Più di una settimana Idoneità per principianti 35/100
I maintainer di solito rispondono entro 1 giorno
-
Meta: separate dense and sparse stores from the expression and constraint frontendForse già presa @Rishabhkanhaiya l’ha presa 4 giorni fa. Apertadiscussion refactor sparse
Difficoltà 5/5 Più di una settimana Idoneità per principianti 25/100
PyPSA/linopy#1019 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
refactor sparse
Difficoltà 4/5 3-5 giorni Idoneità per principianti 38/100
I maintainer di solito rispondono entro 1 giorno
-
discussion math-spec
Difficoltà 5/5 Più di una settimana Idoneità per principianti 30/100
PyPSA/linopy#993 · 2 commenti ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di PyPSA/linopy
Issue simili
-
Claiming namespace `jft63`Apertanamespace operations
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 72/100
EclipseFdn/open-vsx.org#14043 ·
I maintainer di solito rispondono entro 1 giorno
-
netbox status: needs triage type: bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
netbox-community/netbox#23376 ·
I maintainer di solito rispondono entro 1 giorno
-
feedback simulation workshop
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
githubnext/gh-aw-workshop#4455 ·
I maintainer di solito rispondono entro 1 giorno
-
Triage 🩺
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
[BUG] Container scenario crashes without expected_recovery_time, kube DNS example uses retry_waitApertaneeds-triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 77/100
krkn-chaos/krkn#1627 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno