Xpress missing _license_probe: licensed_solvers includes xpress with an out-of-date licence, breaking local tests
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức phù hợp với người mới
- 58/100
- Loại issue
- Lỗi
- Độ rõ ràng
- Khá rõ ràng
- Mức độ hoạt động
- Sôi nổi
- Công nghệ
- python
- Lĩnh vực
- backend, testing-qa
Hướng nghiên cứu
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.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
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.
- Ngôn ngữ chính
- Python
- Star
- 259
- Fork
- 89
- Merge trung bình
- 1 ngày 18 giờ
- Pull request đã merge (30 ngày)
- 26
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của PyPSA/linopy
-
bug solver interface
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
Maintainer thường phản hồi trong vòng 1 ngày
-
refactor sparse
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 35/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Meta: separate dense and sparse stores from the expression and constraint frontendCó thể đã có người làm @Rishabhkanhaiya đã nhận 2 ngày trước. Đang mởdiscussion refactor sparse
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 25/100
PyPSA/linopy#1019 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
refactor sparse
Độ khó 4/5 3-5 ngày Mức phù hợp với người mới 38/100
Maintainer thường phản hồi trong vòng 1 ngày
-
discussion math-spec
Độ khó 5/5 Hơn một tuần Mức phù hợp với người mới 30/100
PyPSA/linopy#993 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Issue tương tự
-
enhancement good first issue Stellar Wave trivial
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
StellarCanary/ProtocolCanary-Fixtures#258 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
github_actions
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 65/100
Hochfrequenz/aibap.mcp#578 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
bug
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 75/100
mishraprafful/multihull#150 ·
Maintainer thường phản hồi trong vòng 1 ngày
-
mp: /status reports the server class name as engine_type, not the configured engineCó thể đã có người làm Có pull request liên kết đang mở hoặc đã được merge. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
Maintainer thường phản hồi trong vòng 2 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 66/100
python-caldav/caldav#735 ·
Maintainer thường phản hồi trong vòng 1 ngày