From 775c035a577a2bd674be9f1a5e7a05884fe1a90e Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Tue, 17 Feb 2026 08:14:16 -0500 Subject: [PATCH 1/2] Add license validation for KNITRO library and update related tests --- include/pyoptinterface/knitro_model.hpp | 2 ++ lib/knitro_model.cpp | 20 ++++++++++++++++++++ lib/knitro_model_ext.cpp | 1 + src/pyoptinterface/knitro.py | 2 ++ tests/conftest.py | 4 ++-- tests/test_knitro.py | 3 ++- 6 files changed, 29 insertions(+), 3 deletions(-) diff --git a/include/pyoptinterface/knitro_model.hpp b/include/pyoptinterface/knitro_model.hpp index 43ec510..a723eef 100644 --- a/include/pyoptinterface/knitro_model.hpp +++ b/include/pyoptinterface/knitro_model.hpp @@ -93,6 +93,8 @@ APILIST bool is_library_loaded(); bool load_library(const std::string &path); + +bool has_valid_license(); } // namespace knitro struct KNITROFreeProblemT diff --git a/lib/knitro_model.cpp b/lib/knitro_model.cpp index 36cdaca..cb69f38 100644 --- a/lib/knitro_model.cpp +++ b/lib/knitro_model.cpp @@ -48,6 +48,26 @@ bool load_library(const std::string &path) return false; } } + +bool has_valid_license() +{ + if (!is_library_loaded()) + { + throw std::runtime_error("KNITRO library not loaded"); + } + + LM_context *lm = nullptr; + int error = KN_checkout_license(&lm); + if (error == 0) + { + KN_release_license(&lm); + return true; + } + else + { + return false; + } +} } // namespace knitro void ensure_library_loaded() diff --git a/lib/knitro_model_ext.cpp b/lib/knitro_model_ext.cpp index 8fb799e..b6bda58 100644 --- a/lib/knitro_model_ext.cpp +++ b/lib/knitro_model_ext.cpp @@ -15,6 +15,7 @@ NB_MODULE(knitro_model_ext, m) m.def("is_library_loaded", &knitro::is_library_loaded); m.def("load_library", &knitro::load_library); + m.def("has_valid_license", &knitro::has_valid_license); bind_knitro_constants(m); diff --git a/src/pyoptinterface/knitro.py b/src/pyoptinterface/knitro.py index 4a947b7..21bebda 100644 --- a/src/pyoptinterface/knitro.py +++ b/src/pyoptinterface/knitro.py @@ -3,6 +3,7 @@ KN, load_library, is_library_loaded, + has_valid_license, ) __all__ = [ @@ -12,4 +13,5 @@ "autoload_library", "load_library", "is_library_loaded", + "has_valid_license", ] diff --git a/tests/conftest.py b/tests/conftest.py index 2bb4fa9..e9b85bb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -23,7 +23,7 @@ def c(): if copt.is_library_loaded(): nlp_model_dict["copt"] = copt.Model -if knitro.is_library_loaded(): +if knitro.is_library_loaded() and knitro.has_valid_license(): nlp_model_dict["knitro"] = knitro.Model @@ -46,7 +46,7 @@ def nlp_model_ctor(request): model_interface_dict["mosek"] = mosek.Model if highs.is_library_loaded(): model_interface_dict["highs"] = highs.Model -if knitro.is_library_loaded(): +if knitro.is_library_loaded() and knitro.has_valid_license(): model_interface_dict["knitro"] = knitro.Model diff --git a/tests/test_knitro.py b/tests/test_knitro.py index 39aa90e..e708010 100644 --- a/tests/test_knitro.py +++ b/tests/test_knitro.py @@ -5,7 +5,8 @@ import pyoptinterface as poi pytestmark = pytest.mark.skipif( - not knitro.is_library_loaded(), reason="KNITRO library is not loaded" + not knitro.is_library_loaded() or not knitro.has_valid_license(), + reason="KNITRO library is not loaded or license is not valid", ) From fa03ceb2cb7f1fc55a09b77e183a84d183762565 Mon Sep 17 00:00:00 2001 From: eminyouskn Date: Tue, 17 Feb 2026 08:49:26 -0500 Subject: [PATCH 2/2] fix merge conflicts --- tests/conftest.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 3852d43..dbfa74a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -45,9 +45,9 @@ def nlp_model_ctor(request): if mosek.is_library_loaded(): model_interface_dict_full["mosek"] = mosek.Model if highs.is_library_loaded(): - model_interface_dict["highs"] = highs.Model + model_interface_dict_full["highs"] = highs.Model if knitro.is_library_loaded() and knitro.has_valid_license(): - model_interface_dict["knitro"] = knitro.Model + model_interface_dict_full["knitro"] = knitro.Model @pytest.fixture(params=model_interface_dict_full.keys())