Skip to content

Commit 835e63c

Browse files
authored
Merge pull request #5 from boegel/ebpythonprefixes2
add test to verify that `$PYTHONPATH` or `$EBPYTHONPREFIXES` are set correctly by generated module file
2 parents 9813bc8 + 5eb6d07 commit 835e63c

File tree

2 files changed

+96
-20
lines changed

2 files changed

+96
-20
lines changed

easybuild/framework/easyblock.py

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1390,6 +1390,37 @@ def make_module_description(self):
13901390
"""
13911391
return self.module_generator.get_description()
13921392

1393+
def make_module_pythonpath(self):
1394+
"""
1395+
Add lines for module file to update $PYTHONPATH or $EBPYTHONPREFIXES,
1396+
if they aren't already present and the standard lib/python*/site-packages subdirectory exists
1397+
"""
1398+
lines = []
1399+
if not os.path.isfile(os.path.join(self.installdir, 'bin', 'python')): # only needed when not a python install
1400+
python_subdir_pattern = os.path.join(self.installdir, 'lib', 'python*', 'site-packages')
1401+
candidate_paths = (os.path.relpath(path, self.installdir) for path in glob.glob(python_subdir_pattern))
1402+
python_paths = [path for path in candidate_paths if re.match(r'lib/python\d+\.\d+/site-packages', path)]
1403+
1404+
runtime_deps = [dep['name'] for dep in self.cfg.dependencies(runtime_only=True)]
1405+
use_ebpythonprefixes = all([
1406+
'Python' in runtime_deps,
1407+
build_option('prefer_python_search_path') == EBPYTHONPREFIXES,
1408+
not self.cfg['force_pythonpath']
1409+
])
1410+
1411+
if python_paths:
1412+
# add paths unless they were already added
1413+
if use_ebpythonprefixes:
1414+
path = '' # EBPYTHONPREFIXES are relative to the install dir
1415+
if path not in self.module_generator.added_paths_per_key[EBPYTHONPREFIXES]:
1416+
lines.append(self.module_generator.prepend_paths(EBPYTHONPREFIXES, path))
1417+
else:
1418+
for python_path in python_paths:
1419+
if python_path not in self.module_generator.added_paths_per_key[PYTHONPATH]:
1420+
lines.append(self.module_generator.prepend_paths(PYTHONPATH, python_path))
1421+
1422+
return lines
1423+
13931424
def make_module_extra(self, altroot=None, altversion=None):
13941425
"""
13951426
Set extra stuff in module file, e.g. $EBROOT*, $EBVERSION*, etc.
@@ -1438,26 +1469,8 @@ def make_module_extra(self, altroot=None, altversion=None):
14381469
value, type(value))
14391470
lines.append(self.module_generator.append_paths(key, value, allow_abs=self.cfg['allow_append_abs_path']))
14401471

1441-
# Add automatic PYTHONPATH or EBPYTHONPREFIXES if they aren't already present and python paths exist
1442-
if not os.path.isfile(os.path.join(self.installdir, 'bin/python')): # only needed when not a python install
1443-
candidate_paths = (os.path.relpath(path, self.installdir)
1444-
for path in glob.glob(f'{self.installdir}/lib/python*/site-packages'))
1445-
python_paths = [path for path in candidate_paths if re.match(r'lib/python\d+\.\d+/site-packages', path)]
1446-
1447-
runtime_deps = [dep['name'] for dep in self.cfg.dependencies(runtime_only=True)]
1448-
use_ebpythonprefixes = 'Python' in runtime_deps and \
1449-
build_option('prefer_python_search_path') == EBPYTHONPREFIXES and not self.cfg['force_pythonpath']
1450-
1451-
if python_paths:
1452-
# Add paths unless they were already added
1453-
if use_ebpythonprefixes:
1454-
path = '' # EBPYTHONPREFIXES are relative to the install dir
1455-
if path not in self.module_generator.added_paths_per_key[EBPYTHONPREFIXES]:
1456-
lines.append(self.module_generator.prepend_paths(EBPYTHONPREFIXES, path))
1457-
else:
1458-
for python_path in python_paths:
1459-
if python_path not in self.module_generator.added_paths_per_key[PYTHONPATH]:
1460-
lines.append(self.module_generator.prepend_paths(PYTHONPATH, python_path))
1472+
# add lines to update $PYTHONPATH or $EBPYTHONPREFIXES
1473+
lines.extend(self.make_module_pythonpath())
14611474

14621475
modloadmsg = self.cfg['modloadmsg']
14631476
if modloadmsg:

test/framework/toy_build.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4238,6 +4238,69 @@ def test_eb_error(self):
42384238
stderr = stderr.getvalue()
42394239
self.assertTrue(regex.search(stderr), f"Pattern '{regex.pattern}' should be found in {stderr}")
42404240

4241+
def test_toy_python(self):
4242+
"""
4243+
Test whether $PYTHONPATH or $EBPYTHONPREFIXES are set correctly.
4244+
"""
4245+
# generate fake Python module that we can use as runtime dependency for toy
4246+
# (required condition for use of $EBPYTHONPREFIXES)
4247+
fake_mods_path = os.path.join(self.test_prefix, 'modules')
4248+
fake_python_mod = os.path.join(fake_mods_path, 'Python', '3.6')
4249+
if get_module_syntax() == 'Lua':
4250+
fake_python_mod += '.lua'
4251+
write_file(fake_python_mod, '')
4252+
else:
4253+
write_file(fake_python_mod, '#%Module')
4254+
self.modtool.use(fake_mods_path)
4255+
4256+
test_ecs = os.path.join(os.path.dirname(__file__), 'easyconfigs', 'test_ecs')
4257+
toy_ec = os.path.join(test_ecs, 't', 'toy', 'toy-0.0.eb')
4258+
4259+
test_ec_txt = read_file(toy_ec)
4260+
test_ec_txt += "\npostinstallcmds.append('mkdir -p %(installdir)s/lib/python3.6/site-packages')"
4261+
test_ec_txt += "\npostinstallcmds.append('touch %(installdir)s/lib/python3.6/site-packages/foo.py')"
4262+
4263+
test_ec = os.path.join(self.test_prefix, 'test.eb')
4264+
write_file(test_ec, test_ec_txt)
4265+
self.run_test_toy_build_with_output(ec_file=test_ec)
4266+
4267+
toy_mod = os.path.join(self.test_installpath, 'modules', 'all', 'toy', '0.0')
4268+
if get_module_syntax() == 'Lua':
4269+
toy_mod += '.lua'
4270+
toy_mod_txt = read_file(toy_mod)
4271+
4272+
pythonpath_regex = re.compile('^prepend.path.*PYTHONPATH.*lib/python3.6/site-packages', re.M)
4273+
4274+
self.assertTrue(pythonpath_regex.search(toy_mod_txt),
4275+
f"Pattern '{pythonpath_regex.pattern}' found in: {toy_mod_txt}")
4276+
4277+
# also check when opting in to use $EBPYTHONPREFIXES instead of $PYTHONPATH
4278+
args = ['--prefer-python-search-path=EBPYTHONPREFIXES']
4279+
self.run_test_toy_build_with_output(ec_file=test_ec, extra_args=args)
4280+
toy_mod_txt = read_file(toy_mod)
4281+
# if Python is not listed as a runtime dependency then $PYTHONPATH is still used,
4282+
# because the Python dependency used must be aware of $EBPYTHONPREFIXES
4283+
# (see sitecustomize.py installed by Python easyblock)
4284+
self.assertTrue(pythonpath_regex.search(toy_mod_txt),
4285+
f"Pattern '{pythonpath_regex.pattern}' found in: {toy_mod_txt}")
4286+
4287+
test_ec_txt += "\ndependencies = [('Python', '3.6', '', SYSTEM)]"
4288+
write_file(test_ec, test_ec_txt)
4289+
self.run_test_toy_build_with_output(ec_file=test_ec, extra_args=args)
4290+
toy_mod_txt = read_file(toy_mod)
4291+
4292+
ebpythonprefixes_regex = re.compile('^prepend.path.*EBPYTHONPREFIXES.*root', re.M)
4293+
self.assertTrue(ebpythonprefixes_regex.search(toy_mod_txt),
4294+
f"Pattern '{ebpythonprefixes_regex.pattern}' found in: {toy_mod_txt}")
4295+
4296+
test_ec_txt += "\nforce_pythonpath = True"
4297+
write_file(test_ec, test_ec_txt)
4298+
self.run_test_toy_build_with_output(ec_file=test_ec)
4299+
toy_mod_txt = read_file(toy_mod)
4300+
4301+
self.assertTrue(pythonpath_regex.search(toy_mod_txt),
4302+
f"Pattern '{pythonpath_regex.pattern}' found in: {toy_mod_txt}")
4303+
42414304

42424305
def suite():
42434306
""" return all the tests in this file """

0 commit comments

Comments
 (0)