Skip to content

Commit

Permalink
Print a warning when mixing $EBPYTHONPREFIXES and $PYTHONPATH modules
Browse files Browse the repository at this point in the history
$PYTHONPATH takes precedence which might lead to unexpected or wrong
packages being used especially when a dependency includes a newer
package than another dependency.
As it is very hard to find this as the cause print a warning when this
situation is found.
  • Loading branch information
Flamefire committed Nov 28, 2024
1 parent 358c719 commit 2777a95
Showing 1 changed file with 35 additions and 2 deletions.
37 changes: 35 additions & 2 deletions easybuild/easyblocks/generic/pythonpackage.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
from easybuild.framework.easyconfig.default import DEFAULT_CONFIG
from easybuild.framework.easyconfig.templates import TEMPLATE_CONSTANTS
from easybuild.framework.extensioneasyblock import ExtensionEasyBlock
from easybuild.tools.build_log import EasyBuildError, print_msg
from easybuild.tools.build_log import EasyBuildError, print_msg, print_warning
from easybuild.tools.config import build_option
from easybuild.tools.filetools import change_dir, mkdir, remove_dir, symlink, which
from easybuild.tools.modules import get_software_root
Expand Down Expand Up @@ -508,6 +508,39 @@ def set_pylibdirs(self):
self.all_pylibdirs = get_pylibdirs(python_cmd=self.python_cmd)
self.pylibdir = self.all_pylibdirs[0]

def verify_eb_pythonprefixes(self):
"""Warn if mixing dependency modules with $PYTHONPATH and $EBPYTHONPREFIXES"""
python_root = get_software_root('Python')
# Our site-customize is installed into the Python module. If that isn't loaded there is nothing to check.
if not python_root:
return
pythonpaths_var = os.environ.get('PYTHONPATH')
if not (os.environ.get(EBPYTHONPREFIXES) and pythonpaths_var):
self.log.debug('At most one of $PYTHONPATH and $%s is set. Skipping conflict check.', EBPYTHONPREFIXES)
return
# Check if any entry from $PYTHONPATH is from a dependent module
pythonpaths = [os.path.realpath(path) for path in pythonpaths_var.split(os.path.pathsep) if os.path.exists(path)]
# Ignore any paths that cannot be replaced by $EBPYTHONPREFIXES to avoid false positives
pythonpaths = [path for path in pythonpaths if path.endswith(os.sep + self.pylibdir)]
self.log.debug('Keeping %s from $PYTHONPATH=%s for checking', pythonpaths, pythonpaths_var)

def is_in_pythonpaths(dependency):
if dependency['name'] == 'Python':
return False # Ignore the Python module
try:
root = os.path.realpath(get_software_root(dependency['name']))
except OSError:
return False
# Search for the path or a subdirectory of it
return any(path == root or path.startswith(root + os.sep) for path in pythonpaths)

conflicts = [dep['name'] for dep in self.cfg.dependencies() if is_in_pythonpaths(dep)]
if conflicts:
print_warning('The following modules set $PYTHONPATH but $%s is also used: %s\n'
'Mixing such modules might cause unexpected results during the build of Python packages. '
'Try rebuilding the module files to use either $PYTHONPATH or $%s.',
EBPYTHONPREFIXES, ', '.join(conflicts), EBPYTHONPREFIXES)

def prepare_python(self):
"""Python-specific preparations."""

Expand Down Expand Up @@ -562,8 +595,8 @@ def prepare_python(self):
self.log.info("Python command being used: %s", self.python_cmd)

if self.python_cmd:
# set Python lib directories
self.set_pylibdirs()
self.verify_eb_pythonprefixes()

def _should_unpack_source(self):
"""Determine whether we need to unpack the source(s)"""
Expand Down

0 comments on commit 2777a95

Please sign in to comment.