Skip to content

Commit b28face

Browse files
committed
Allow Path as argument for Executable feature
to make it easier to specify the absolute path of the nauty exe
1 parent 0751f4c commit b28face

File tree

2 files changed

+37
-18
lines changed

2 files changed

+37
-18
lines changed

src/sage/features/__init__.py

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ def absolute_filename(self) -> str:
657657

658658
class Executable(FileFeature):
659659
r"""
660-
A feature describing an executable in the ``PATH``.
660+
A feature describing an executable.
661661
662662
In an installation of Sage with ``SAGE_LOCAL`` different from ``SAGE_VENV``, the
663663
executable is searched first in ``SAGE_VENV/bin``, then in ``SAGE_LOCAL/bin``,
@@ -680,7 +680,9 @@ class Executable(FileFeature):
680680
sage: Executable(name='does-not-exist', executable='does-not-exist-xxxxyxyyxyy').is_present()
681681
FeatureTestResult('does-not-exist', False)
682682
"""
683-
def __init__(self, name, executable, **kwds):
683+
executable: Path
684+
685+
def __init__(self, name: str, executable: Path | str, **kwds) -> None:
684686
r"""
685687
TESTS::
686688
@@ -689,9 +691,9 @@ def __init__(self, name, executable, **kwds):
689691
True
690692
"""
691693
Feature.__init__(self, name, **kwds)
692-
self.executable = executable
694+
self.executable = Path(executable)
693695

694-
def _is_present(self):
696+
def _is_present(self) -> FeatureTestResult:
695697
r"""
696698
Test whether the executable is on the current PATH and functional.
697699
@@ -708,7 +710,7 @@ def _is_present(self):
708710
return result
709711
return self.is_functional()
710712

711-
def is_functional(self):
713+
def is_functional(self) -> FeatureTestResult:
712714
r"""
713715
Return whether an executable in the path is functional.
714716
@@ -744,22 +746,31 @@ def absolute_filename(self) -> str:
744746
sage.features.FeatureNotPresentError: does-not-exist is not available.
745747
Executable 'does-not-exist-xxxxyxyyxyy' not found on PATH.
746748
"""
747-
if SAGE_LOCAL:
748-
if Path(SAGE_VENV).resolve() != Path(SAGE_LOCAL).resolve():
749-
# As sage.env currently gives SAGE_LOCAL a fallback value from SAGE_VENV,
750-
# SAGE_LOCAL is never unset. So we only use it if it differs from SAGE_VENV.
751-
search_path = ':'.join([os.path.join(SAGE_VENV, 'bin'),
752-
os.path.join(SAGE_LOCAL, 'bin')])
753-
path = shutil.which(self.executable, path=search_path)
754-
if path is not None:
755-
return path
749+
if self.executable.is_absolute() and self.executable.exists():
750+
return str(self.executable.resolve())
751+
752+
if (
753+
SAGE_LOCAL
754+
and SAGE_VENV
755+
and Path(SAGE_VENV).resolve() != Path(SAGE_LOCAL).resolve()
756+
):
757+
# As sage.env currently gives SAGE_LOCAL a fallback value from SAGE_VENV,
758+
# SAGE_LOCAL is never unset. So we only use it if it differs from SAGE_VENV.
759+
search_paths = os.pathsep.join(
760+
[os.path.join(SAGE_VENV, "bin"), os.path.join(SAGE_LOCAL, "bin")]
761+
)
762+
path = shutil.which(self.executable, path=search_paths)
763+
if path is not None:
764+
return path
756765
# Now look up in the regular PATH.
757766
path = shutil.which(self.executable)
758767
if path is not None:
759768
return path
760-
raise FeatureNotPresentError(self,
761-
reason="Executable {executable!r} not found on PATH.".format(executable=self.executable),
762-
resolution=self.resolution())
769+
raise FeatureNotPresentError(
770+
self,
771+
reason=f"Executable {self.executable!r} not found on PATH.",
772+
resolution=self.resolution(),
773+
)
763774

764775

765776
class StaticFile(FileFeature):

src/sage/features/nauty.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
# https://www.gnu.org/licenses/
1212
# *****************************************************************************
1313

14+
from pathlib import Path
15+
1416
from sage.env import SAGE_NAUTY_BINS_PREFIX
1517

1618
from . import Executable
@@ -36,10 +38,16 @@ def __init__(self, name):
3638
sage: isinstance(NautyExecutable('geng'), NautyExecutable)
3739
True
3840
"""
41+
if SAGE_NAUTY_BINS_PREFIX is None:
42+
raise ValueError("SAGE_NAUTY_BINS_PREFIX is not set.")
43+
if SAGE_NAUTY_BINS_PREFIX.endswith("-"):
44+
executable = f"{SAGE_NAUTY_BINS_PREFIX}{name}"
45+
else:
46+
executable = Path(SAGE_NAUTY_BINS_PREFIX) / name
3947
Executable.__init__(
4048
self,
4149
name=f"nauty_{name}",
42-
executable=f"{SAGE_NAUTY_BINS_PREFIX}{name}",
50+
executable=executable,
4351
spkg="nauty",
4452
type="standard",
4553
)

0 commit comments

Comments
 (0)