Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 30 additions & 9 deletions easybuild/tools/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,18 +189,39 @@ def sanitize_env():
"""
Sanitize environment.

This function undefines all $PYTHON* environment variables,
since they may affect the build/install procedure of Python packages.
This function:

cfr. https://docs.python.org/2/using/cmdline.html#environment-variables
* Filters out empty entries from environment variables like $PATH, $LD_LIBRARY_PATH, etc.
Empty entries make no sense, and can cause problems,
see for example https://github.com/easybuilders/easybuild-easyconfigs/issues/9843 .

While the $PYTHON* environment variables may be relevant/required for EasyBuild itself,
and for any non-stdlib Python packages it uses,
they are irrelevant (and potentially harmful) when installing Python packages.
* Undefines all $PYTHON* environment variables,
since they may affect the build/install procedure of Python packages.

Note that this is not an airtight protection against the Python being used in the build/install procedure
picking up non-stdlib Python packages (e.g., setuptools, vsc-base, ...), thanks to the magic of .pth files,
cfr. https://docs.python.org/2/library/site.html .
cfr. https://docs.python.org/2/using/cmdline.html#environment-variables

While the $PYTHON* environment variables may be relevant/required for EasyBuild itself,
and for any non-stdlib Python packages it uses,
they are irrelevant (and potentially harmful) when installing Python packages.

Note that this is not an airtight protection against the Python being used in the build/install procedure
picking up non-stdlib Python packages (e.g., setuptools, vsc-base, ...), thanks to the magic of .pth files,
cfr. https://docs.python.org/2/library/site.html .
"""

# remove empty entries from $*PATH variables
for key in ['CPATH', 'LD_LIBRARY_PATH', 'LIBRARY_PATH', 'LD_PRELOAD', 'PATH']:
val = os.getenv(key)
if val:
entries = val.split(os.pathsep)
if '' in entries:
_log.info("Found %d empty entries in $%s, filtering them out...", entries.count(''), key)
newval = os.pathsep.join(x for x in entries if x)
if newval:
setvar(key, newval)
else:
unset_env_vars([key], verbose=False)

# unset all $PYTHON* environment variables
keys_to_unset = [key for key in os.environ if key.startswith('PYTHON')]
unset_env_vars(keys_to_unset, verbose=False)
31 changes: 31 additions & 0 deletions test/framework/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,37 @@ def test_unset_env_vars(self):
}
self.assertEqual(res, expected)

def test_sanitize_env(self):
"""Test sanitize_env function."""

# define $*PATH variable that include empty entries, those should get filtered out
os.environ['PATH'] = '/bar::/foo:' + self.test_prefix # middle empty entry
os.environ['LD_LIBRARY_PATH'] = '/apps/slurm/default/lib:/usr/lib:' # trailing empty entry
os.environ['LIBRARY_PATH'] = self.test_prefix + ':' + os.environ['HOME'] # no empty entries here
os.environ['CPATH'] = ':' + self.test_prefix # leading empty entry
os.environ['LD_PRELOAD'] = ':::' # only empty entries (should get unset!)

# define $PYTHON* environment variables, these should be unset by sanitize_env
os.environ['PYTHONNOUSERSITE'] = '1'
os.environ['PYTHONPATH'] = self.test_prefix
os.environ['PYTHONOPTIMIZE'] = '1'

env.sanitize_env()

self.assertFalse(any(x for x in os.environ.keys() if x.startswith('PYTHON')))

expected = {
'CPATH': self.test_prefix,
'LD_LIBRARY_PATH': '/apps/slurm/default/lib:/usr/lib',
'LIBRARY_PATH': self.test_prefix + ':' + os.environ['HOME'],
'PATH': '/bar:/foo:' + self.test_prefix,
}
for key in sorted(expected):
self.assertEqual(os.getenv(key), expected[key])
self.assertEqual(os.environ[key], expected[key])

self.assertEqual(os.getenv('LD_PRELOAD'), None)


def suite():
""" returns all the testcases in this module """
Expand Down