Skip to content

Commit 41ea350

Browse files
authored
Merge pull request #3953 from Thyre/20251009223823_new_pr_pythonpackage
enhance PythonPackage & PythonBundle easyblock to consider EBPYTHONPREFIXES for test installs and path configuration files
2 parents c749b82 + 70e4828 commit 41ea350

File tree

2 files changed

+60
-17
lines changed

2 files changed

+60
-17
lines changed

easybuild/easyblocks/generic/pythonbundle.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
from easybuild.tools.build_log import EasyBuildError
3636
from easybuild.tools.config import build_option, PYTHONPATH, EBPYTHONPREFIXES
3737
from easybuild.tools.modules import get_software_root
38+
from easybuild.tools.filetools import search_file
3839

3940

4041
class PythonBundle(Bundle):
@@ -123,6 +124,19 @@ def make_module_extra(self, *args, **kwargs):
123124
self.log.info("Preferred Python search path is $EBPYTHONPREFIXES, so using that")
124125
use_ebpythonprefixes = True
125126

127+
# Check if the installdir or sources contain any .pth files. For them to work correctly,
128+
# Python needs these files to be in the sitedir path. While this typically works system-wide
129+
# or in a venv, having Python modules in separate directories is unusual, and only having
130+
# $PYTHONPATH will ignore these files.
131+
# Our sitecustomize.py adds paths in $EBPYTHONPREFIXES to the sitedir path though, allowing
132+
# these .pth files to work as expected. See: https://docs.python.org/3/library/site.html#module-site
133+
# .pth files always should be in the site folder, so most of the path is fixed.
134+
# Try the installation directory first
135+
if self.installdir and search_file([self.installdir], r".*\.pth$", silent=True):
136+
self.log.info(f"Found path configuration file in installation directory '{self.installdir}'. "
137+
"Enabling $EBPYTHONPREFIXES...")
138+
use_ebpythonprefixes = True
139+
126140
if self.multi_python or use_ebpythonprefixes:
127141
path = '' # EBPYTHONPREFIXES are relative to the install dir
128142
if path not in self.module_generator.added_paths_per_key[EBPYTHONPREFIXES]:

easybuild/easyblocks/generic/pythonpackage.py

Lines changed: 46 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
from easybuild.framework.extensioneasyblock import ExtensionEasyBlock
5050
from easybuild.tools.build_log import EasyBuildError, print_msg
5151
from easybuild.tools.config import build_option, PYTHONPATH, EBPYTHONPREFIXES
52-
from easybuild.tools.filetools import change_dir, mkdir, read_file, remove_dir, symlink, which, write_file
52+
from easybuild.tools.filetools import change_dir, mkdir, read_file, remove_dir, symlink, which, write_file, search_file
5353
from easybuild.tools.modules import ModEnvVarType, get_software_root
5454
from easybuild.tools.run import run_shell_cmd
5555
from easybuild.tools.utilities import nub
@@ -618,6 +618,46 @@ def using_local_py_install_scheme(self):
618618
py_install_scheme = det_py_install_scheme(python_cmd=self.python_cmd)
619619
return py_install_scheme == PY_INSTALL_SCHEME_POSIX_LOCAL and self.using_pip_install()
620620

621+
def should_use_ebpythonprefixes(self) -> bool:
622+
"""
623+
Determine if we should update $EBPYTHONPREFIXES rather than $PYTHONPATH.
624+
If this Python package was installed for multiple Python versions, the package is using
625+
.pth files, or if we prefer it; note: although EasyBuild framework also has logic for
626+
this in EasyBlock.make_module_extra, we retain full control here, since the logic is
627+
slightly different.
628+
"""
629+
630+
use_ebpythonprefixes = False
631+
runtime_deps = [dep['name'] for dep in self.cfg.dependencies(runtime_only=True)]
632+
633+
if 'Python' in runtime_deps:
634+
self.log.info("Found Python runtime dependency, so considering $EBPYTHONPREFIXES...")
635+
if build_option('prefer_python_search_path') == EBPYTHONPREFIXES:
636+
self.log.info("Preferred Python search path is $EBPYTHONPREFIXES, so using that")
637+
use_ebpythonprefixes = True
638+
639+
# Check if the installdir or sources contain any .pth files. For them to work correctly,
640+
# Python needs these files to be in the sitedir path. While this typically works system-wide
641+
# or in a venv, having Python modules in separate directories is unusual, and only having
642+
# $PYTHONPATH will ignore these files.
643+
# Our sitecustomize.py adds paths in $EBPYTHONPREFIXES to the sitedir path though, allowing
644+
# these .pth files to work as expected. See: https://docs.python.org/3/library/site.html#module-site
645+
# .pth files always should be in the site folder, so most of the path is fixed.
646+
# Try the installation directory first
647+
if self.installdir and search_file([self.installdir], r".*\.pth$", silent=True):
648+
self.log.info(f"Found path configuration file in installation directory '{self.installdir}'. "
649+
"Enabling $EBPYTHONPREFIXES...")
650+
use_ebpythonprefixes = True
651+
# If we did a test installation, check that one as well. Ensure that pypkg_test_installdir is set,
652+
# since that might not be the case for sanity_check_only or module_only.
653+
if self.testinstall and self.pypkg_test_installdir:
654+
if search_file([self.pypkg_test_installdir], r".*\.pth$", silent=True):
655+
self.log.info("Found path configuration file in test installation directory "
656+
f"'{self.pypkg_test_installdir}'. Enabling $EBPYTHONPREFIXES...")
657+
use_ebpythonprefixes = True
658+
659+
return self.multi_python or use_ebpythonprefixes
660+
621661
def compose_install_command(self, prefix, extrapath=None, installopts=None):
622662
"""Compose full install command."""
623663

@@ -824,7 +864,7 @@ def test_step(self, return_output_ec=False):
824864
out, ec = (None, None)
825865

826866
if self.testinstall:
827-
# install in test directory and export PYTHONPATH
867+
# install in test directory and export PYTHONPATH and / or EBPYTHONPREFIX if we need it
828868

829869
try:
830870
if self.pypkg_test_installdir is None:
@@ -852,7 +892,9 @@ def test_step(self, return_output_ec=False):
852892
# add install location to both $PYTHONPATH and $PATH
853893
abs_pylibdirs = [os.path.join(actual_installdir, pylibdir) for pylibdir in self.all_pylibdirs]
854894
extrapath = "export PYTHONPATH=%s && " % os.pathsep.join(abs_pylibdirs + ['$PYTHONPATH'])
855-
895+
if self.should_use_ebpythonprefixes():
896+
extrapath += "export EBPYTHONPREFIXES=%s && " % os.pathsep.join([self.pypkg_test_installdir] +
897+
['$EBPYTHONPREFIXES'])
856898
extrapath += "export PATH=%s:$PATH && " % os.path.join(actual_installdir, 'bin')
857899

858900
cmd = self.compose_install_command(self.pypkg_test_installdir, extrapath=extrapath)
@@ -1087,20 +1129,7 @@ def make_module_extra(self, *args, **kwargs):
10871129
"""Add install path to PYTHONPATH"""
10881130
txt = ''
10891131

1090-
# update $EBPYTHONPREFIXES rather than $PYTHONPATH
1091-
# if this Python package was installed for multiple Python versions, or if we prefer it;
1092-
# note: although EasyBuild framework also has logic for this in EasyBlock.make_module_extra,
1093-
# we retain full control here, since the logic is slightly different
1094-
use_ebpythonprefixes = False
1095-
runtime_deps = [dep['name'] for dep in self.cfg.dependencies(runtime_only=True)]
1096-
1097-
if 'Python' in runtime_deps:
1098-
self.log.info("Found Python runtime dependency, so considering $EBPYTHONPREFIXES...")
1099-
if build_option('prefer_python_search_path') == EBPYTHONPREFIXES:
1100-
self.log.info("Preferred Python search path is $EBPYTHONPREFIXES, so using that")
1101-
use_ebpythonprefixes = True
1102-
1103-
if self.multi_python or use_ebpythonprefixes:
1132+
if self.should_use_ebpythonprefixes():
11041133
path = '' # EBPYTHONPREFIXES are relative to the install dir
11051134
txt += self.module_generator.prepend_paths(EBPYTHONPREFIXES, path)
11061135
elif self.require_python:

0 commit comments

Comments
 (0)