diff --git a/easybuild/tools/environment.py b/easybuild/tools/environment.py index 18e8c798d5..5a6e2e39dc 100644 --- a/easybuild/tools/environment.py +++ b/easybuild/tools/environment.py @@ -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) diff --git a/test/framework/environment.py b/test/framework/environment.py index 7782067d13..7e0ca9ccc8 100644 --- a/test/framework/environment.py +++ b/test/framework/environment.py @@ -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 """