Compare commits

...

3 Commits

Author SHA1 Message Date
Todd Gamblin
56c947722e
not == to !=
Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2025-04-09 11:18:43 -07:00
Todd Gamblin
b14f172a61
check spec name
Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2025-04-09 09:49:26 -07:00
Todd Gamblin
795335d56e
python: factor out find_python_in_prefix
Pull a free-standing `find_python_in_prefix` function out of `python`'s `command()`
property.

Originally done for #44382 but not ultimately used, I still think this is a good
refactor, so submitting as a separate pull request.

Signed-off-by: Todd Gamblin <tgamblin@llnl.gov>
2025-04-02 11:41:12 -07:00

View File

@ -10,8 +10,9 @@
import subprocess import subprocess
import sys import sys
from shutil import copy from shutil import copy
from typing import Dict, List from typing import Dict, List, Optional
import llnl.util.filesystem as fs
from llnl.util.lang import dedupe from llnl.util.lang import dedupe
from spack.build_environment import dso_suffix, stat_suffix from spack.build_environment import dso_suffix, stat_suffix
@ -37,6 +38,53 @@ def make_pyvenv_cfg(python_pkg: Package, venv_prefix: str) -> str:
return "\n".join(lines) + "\n" return "\n".join(lines) + "\n"
def find_python_in_prefix(spec: "spack.spec.Spec", prefix: Optional[str] = None) -> Executable:
"""Finds a python command in a prefix, given a Spec for the python installation.
The python command may vary depending on the version of Python and how it was
installed. In general, Python 3 only comes with a ``python3`` command. However, some
package managers will symlink ``python`` to ``python3``, while others may contain
``python3.11``, ``python3.10``, and ``python3.9`` in the same directory.
Returns:
Executable: the Python command
"""
if spec.name != "python":
raise ValueError("find_python_in_prefix() only works on Python installations.")
prefix = Prefix(prefix or spec.prefix)
# We need to be careful here. If the user is using an externally
# installed python, several different commands could be located
# in the same directory. Be as specific as possible. Search for:
#
# * python3.11
# * python3
# * python
#
# in that order if using python@3.11.0, for example.
# in that order if using python@3.11.0, for example.
suffixes = [spec.version.up_to(2), spec.version.up_to(1), ""]
file_extension = "" if sys.platform != "win32" else ".exe"
patterns = [f"python{ver}{file_extension}" for ver in suffixes]
root = prefix.bin if sys.platform != "win32" else prefix
path = fs.find_first(root, files=patterns)
if path is not None:
return Executable(path)
else:
# Give a last try at rhel8 platform python
if spec.external and prefix == "/usr" and spec.satisfies("os=rhel8"):
path = os.path.join(prefix, "libexec", "platform-python")
if os.path.exists(path):
return Executable(path)
raise RuntimeError(f"Unable to locate python command in {prefix} or its subdirectories")
class Python(Package): class Python(Package):
"""The Python programming language.""" """The Python programming language."""
@ -906,46 +954,9 @@ def import_tests(self):
# ======================================================================== # ========================================================================
@property @property
def command(self): def command(self) -> Executable:
"""Returns the Python command, which may vary depending """Returns the Python command, using ``find_python_in_prefix``."""
on the version of Python and how it was installed. return find_python_in_prefix(self.spec)
In general, Python 3 only comes with a ``python3`` command. However, some
package managers will symlink ``python`` to ``python3``, while others
may contain ``python3.11``, ``python3.10``, and ``python3.9`` in the
same directory.
Returns:
Executable: the Python command
"""
# We need to be careful here. If the user is using an externally
# installed python, several different commands could be located
# in the same directory. Be as specific as possible. Search for:
#
# * python3.11
# * python3
# * python
#
# in that order if using python@3.11.0, for example.
suffixes = [self.spec.version.up_to(2), self.spec.version.up_to(1), ""]
file_extension = "" if sys.platform != "win32" else ".exe"
patterns = [f"python{ver}{file_extension}" for ver in suffixes]
root = self.prefix.bin if sys.platform != "win32" else self.prefix
path = find_first(root, files=patterns)
if path is not None:
return Executable(path)
else:
# Give a last try at rhel8 platform python
if self.spec.external and self.prefix == "/usr" and self.spec.satisfies("os=rhel8"):
path = os.path.join(self.prefix, "libexec", "platform-python")
if os.path.exists(path):
return Executable(path)
raise RuntimeError(
f"cannot to locate the '{self.name}' command in {root} or its subdirectories"
)
@property @property
def config_vars(self): def config_vars(self):