From 766e62ebb43fa0a9573d1c81cd9e619a79b7d769 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 4 Aug 2025 20:16:24 -0400 Subject: [PATCH 1/5] src/sage/features/sagemath.py: new feature for sage.libs.eclib The meson build system is now capable of building sagelib without sage.libs.eclib. Here we add a new feature to represent it. In particular this allows us to use "needs sage.libs.eclib" in tests. --- src/sage/features/sagemath.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/sage/features/sagemath.py b/src/sage/features/sagemath.py index 6313f1c715a..6940344dc5b 100644 --- a/src/sage/features/sagemath.py +++ b/src/sage/features/sagemath.py @@ -512,6 +512,33 @@ def __init__(self): spkg='sagemath_ntl', type='standard') +class sage__libs__eclib(JoinFeature): + r""" + A :class:`sage.features.Feature` describing the presence of + :mod:`sage.libs.eclib`. + + In addition to the modularization purposes that this tag serves, + it also provides attribution to the upstream project. + + TESTS:: + + sage: from sage.features.sagemath import sage__libs__eclib + sage: sage__libs__eclib().is_present() # needs sage.libs.eclib + FeatureTestResult('sage.libs.eclib', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.sagemath import sage__libs__eclib + sage: isinstance(sage__libs__eclib(), sage__libs__eclib) + True + """ + JoinFeature.__init__(self, 'sage.libs.eclib', + [PythonModule('sage.libs.eclib.mwrank')], + spkg='eclib', type='standard') + + class sage__libs__giac(JoinFeature): r""" A :class:`sage.features.Feature` describing the presence of :mod:`sage.libs.giac`. From 43cc494fcced030c741f8dffde41caed070bcfb4 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 4 Aug 2025 20:18:58 -0400 Subject: [PATCH 2/5] src/sage/features/mwrank.py: new feature for the mwrank program The meson build system is now capable of building sagelib without linking to libec, which means that the mwrank program may not be installed. Here we add a new feature to represent it. In particular this allows us to use "needs mwrank" in tests. --- src/sage/features/mwrank.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 src/sage/features/mwrank.py diff --git a/src/sage/features/mwrank.py b/src/sage/features/mwrank.py new file mode 100644 index 00000000000..39e3e0af485 --- /dev/null +++ b/src/sage/features/mwrank.py @@ -0,0 +1,33 @@ +r""" +Feature for testing the presence of the ``mwrank`` program (part +of eclib) +""" + +from . import Executable, FeatureTestResult + + +class Mwrank(Executable): + r""" + A :class:`~sage.features.Feature` describing the presence of + the ``mwrank`` program. + + EXAMPLES:: + + sage: from sage.features.mwrank import Mwrank + sage: Mwrank().is_present() # needs mwrank + FeatureTestResult('mwrank', True) + """ + def __init__(self): + r""" + TESTS:: + + sage: from sage.features.mwrank import Mwrank + sage: isinstance(Mwrank(), Mwrank) + True + """ + Executable.__init__(self, 'mwrank', executable='mwrank', + spkg='eclib', type='standard') + + +def all_features(): + return [Mwrank()] From c82a4163533c796e53ca41e08107d8efeb3a7140 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Sun, 7 Sep 2025 14:05:25 -0400 Subject: [PATCH 3/5] src/doc/en/reference/spkg/index.rst: add sage.features.mwrank --- src/doc/en/reference/spkg/index.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/en/reference/spkg/index.rst b/src/doc/en/reference/spkg/index.rst index e9ba934a540..cc5f4ad00cd 100644 --- a/src/doc/en/reference/spkg/index.rst +++ b/src/doc/en/reference/spkg/index.rst @@ -52,6 +52,7 @@ Features sage/features/mcqd sage/features/meataxe sage/features/mip_backends + sage/features/mwrank sage/features/normaliz sage/features/pandoc sage/features/pdf2svg From 11fedce649850faf8ac24a8006a2829792ca8ed6 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 13 Oct 2025 16:42:39 -0400 Subject: [PATCH 4/5] conftest.py: only ignore ImportErrors for disabled features If the feature for sage.libs.foo is enabled, we shouldn't skip over files that fail to collect with an ImportError for sage.libs.foo. --- conftest.py | 35 +++++++++++++++++++++++++++-------- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/conftest.py b/conftest.py index 5ab4ef025d4..54a9910c4b1 100644 --- a/conftest.py +++ b/conftest.py @@ -116,14 +116,33 @@ def _find( pytest.skip("unable to import module %r" % self.path) else: if isinstance(exception, ModuleNotFoundError): - # Ignore some missing features/modules for now - # TODO: Remove this once all optional things are using Features - if exception.name in ( - "valgrind", - "rpy2", - "sage.libs.coxeter3.coxeter", - "sagemath_giac", - ): + # Ignore some missing features/modules for + # now. Many of these are using custom + # "sage.doctest" headers that only our doctest + # runner (i.e. not pytest) can understand. + # + # TODO: we should remove this once all + # optional things are using Features. It + # wouldn't be too hard to move the + # "sage.doctest" header into pytest (as + # explicit ignore lists based on feature + # tests), but that would require duplication + # for as long as `sage -t` is still used. + from sage.features.coxeter3 import Coxeter3 + from sage.features.sagemath import sage__libs__giac + from sage.features.standard import PythonModule + + exc_list = ["valgrind"] + if not PythonModule("rpy2").is_present(): + exc_list.append("rpy2") + if not Coxeter3().is_present(): + exc_list.append("sage.libs.coxeter3.coxeter") + if not sage__libs__giac().is_present(): + exc_list.append("sagemath_giac") + + # Ignore import errors, but only when the associated + # feature is actually disabled. + if exception.name in exc_list: pytest.skip( f"unable to import module {self.path} due to missing feature {exception.name}" ) From 121c52efd65e51b8a86275f37984639f8b8ae9f9 Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Mon, 13 Oct 2025 16:44:41 -0400 Subject: [PATCH 5/5] conftest.py: ignore ImportErrors for sage.libs.eclib if disabled When sage.libs.eclib doesn't exist, pytest will still try to collect *.py files that import it, leading to an ImportError. We add eclib to the list of feature-backed modules with workarounds for this issue. --- conftest.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/conftest.py b/conftest.py index 54a9910c4b1..4ddcbc87cba 100644 --- a/conftest.py +++ b/conftest.py @@ -129,10 +129,13 @@ def _find( # tests), but that would require duplication # for as long as `sage -t` is still used. from sage.features.coxeter3 import Coxeter3 - from sage.features.sagemath import sage__libs__giac + from sage.features.sagemath import (sage__libs__eclib, + sage__libs__giac) from sage.features.standard import PythonModule exc_list = ["valgrind"] + if not sage__libs__eclib().is_present(): + exc_list.append("sage.libs.eclib") if not PythonModule("rpy2").is_present(): exc_list.append("rpy2") if not Coxeter3().is_present(): @@ -141,8 +144,9 @@ def _find( exc_list.append("sagemath_giac") # Ignore import errors, but only when the associated - # feature is actually disabled. - if exception.name in exc_list: + # feature is actually disabled. Use startswith() so + # that sage.libs.foo matches all of sage.libs.foo.* + if any(exception.name.startswith(e) for e in exc_list): pytest.skip( f"unable to import module {self.path} due to missing feature {exception.name}" )