-
Notifications
You must be signed in to change notification settings - Fork 216
Prefer $EBPYTHONPREFIXES over $PYTHONPATH
#4496
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 9 commits
0ec2df5
74a3cb3
03e304e
b07ea3f
8c66e1a
ccd3d7e
f76f320
8b5389f
cb6adb0
4a11f2f
c471e03
2676e4e
f4fbe76
92f0e62
e91186e
236c67f
ca304fb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -213,22 +213,14 @@ def _filter_paths(self, key, paths): | |||||
| return paths | ||||||
|
|
||||||
| added_paths = self.added_paths_per_key.setdefault(key, set()) | ||||||
| # paths can be a string | ||||||
| if isinstance(paths, str): | ||||||
| if paths in added_paths: | ||||||
| filtered_paths = None | ||||||
| else: | ||||||
| added_paths.add(paths) | ||||||
| filtered_paths = paths | ||||||
| else: | ||||||
| # Coerce any iterable/generator into a list | ||||||
| if not isinstance(paths, list): | ||||||
| paths = list(paths) | ||||||
| filtered_paths = [x for x in paths if x not in added_paths and not added_paths.add(x)] | ||||||
| # Coerce any iterable/generator into a list | ||||||
| if not isinstance(paths, list): | ||||||
| paths = list(paths) | ||||||
| filtered_paths = [x for x in paths if x not in added_paths and not added_paths.add(x)] | ||||||
| if filtered_paths != paths: | ||||||
| removed_paths = paths if filtered_paths is None else [x for x in paths if x not in filtered_paths] | ||||||
| print_warning("Suppressed adding the following path(s) to $%s of the module as they were already added: %s", | ||||||
Micket marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
| key, removed_paths, | ||||||
| key, ', '.join(removed_paths), | ||||||
| log=self.log) | ||||||
| if not filtered_paths: | ||||||
| filtered_paths = None | ||||||
|
|
@@ -243,9 +235,6 @@ def append_paths(self, key, paths, allow_abs=False, expand_relpaths=True): | |||||
| :param allow_abs: allow providing of absolute paths | ||||||
| :param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir) | ||||||
| """ | ||||||
| paths = self._filter_paths(key, paths) | ||||||
| if paths is None: | ||||||
| return '' | ||||||
| return self.update_paths(key, paths, prepend=False, allow_abs=allow_abs, expand_relpaths=expand_relpaths) | ||||||
|
|
||||||
| def prepend_paths(self, key, paths, allow_abs=False, expand_relpaths=True): | ||||||
|
|
@@ -257,11 +246,38 @@ def prepend_paths(self, key, paths, allow_abs=False, expand_relpaths=True): | |||||
| :param allow_abs: allow providing of absolute paths | ||||||
| :param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir) | ||||||
| """ | ||||||
| paths = self._filter_paths(key, paths) | ||||||
| if paths is None: | ||||||
| return '' | ||||||
| return self.update_paths(key, paths, prepend=True, allow_abs=allow_abs, expand_relpaths=expand_relpaths) | ||||||
|
|
||||||
| def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True): | ||||||
| """ | ||||||
| Generate append/prepend-path statements for the given list of paths. | ||||||
|
|
||||||
| :param key: environment variable to append paths to | ||||||
| :param paths: list of paths to append | ||||||
| :param allow_abs: allow providing of absolute paths | ||||||
| :param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir) | ||||||
| """ | ||||||
| if isinstance(paths, str): | ||||||
| self.log.debug("Wrapping %s into a list before using it for %s", paths, key) | ||||||
| paths = [paths] | ||||||
|
|
||||||
| if key == 'PYTHONPATH': | ||||||
| python_paths = [path for path in paths if re.match(r'lib/python\d+\.\d+/site-packages', path)] | ||||||
|
||||||
| if len(python_paths) > 1: | ||||||
| raise EasyBuildError('Found multiple paths for PYTHONPATH: ' + ', '.join(python_paths)) | ||||||
|
|
||||||
| # Special condition for PYTHONPATHs that match the standard pattern, | ||||||
| # replace with EBPYTHONPREFIX which is added to python sys path at runtime | ||||||
| if python_paths and build_option('prefer_ebpythonprefix_over_pythonpath'): | ||||||
| python_path = python_paths[0] | ||||||
| self.log.info("Replaced PYTHONPATH %s with EBPYTHONPREFIXES", python_path) | ||||||
| ret = self._update_paths('EBPYTHONPREFIXES', [''], prepend, allow_abs, expand_relpaths) | ||||||
| paths = [path for path in paths if path != python_path] | ||||||
| if paths: | ||||||
| ret += self._update_paths(key, paths, prepend, allow_abs, expand_relpaths) | ||||||
| return ret | ||||||
| return self._update_paths(key, paths, prepend, allow_abs, expand_relpaths) | ||||||
|
|
||||||
| def _modulerc_check_module_version(self, module_version): | ||||||
| """ | ||||||
| Check value type & contents of specified module-version spec. | ||||||
|
|
@@ -551,7 +567,7 @@ def unload_module(self, mod_name): | |||||
| """ | ||||||
| raise NotImplementedError | ||||||
|
|
||||||
| def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True): | ||||||
| def _update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True): | ||||||
| """ | ||||||
| Generate prepend-path or append-path statements for the given list of paths. | ||||||
|
|
||||||
|
|
@@ -955,7 +971,7 @@ def msg_on_unload(self, msg): | |||||
| print_cmd = "puts stderr %s" % quote_str(msg, tcl=True) | ||||||
| return '\n'.join(['', self.conditional_statement("module-info mode unload", print_cmd, indent=False)]) | ||||||
|
|
||||||
| def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True): | ||||||
| def _update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpaths=True): | ||||||
| """ | ||||||
| Generate prepend-path or append-path statements for the given list of paths. | ||||||
|
|
||||||
|
|
@@ -965,6 +981,10 @@ def update_paths(self, key, paths, prepend=True, allow_abs=False, expand_relpath | |||||
| :param allow_abs: allow providing of absolute paths | ||||||
| :param expand_relpaths: expand relative paths into absolute paths (by prefixing install dir) | ||||||
| """ | ||||||
| paths = self._filter_paths(key, paths) | ||||||
| if paths is None: | ||||||
|
||||||
| if paths is None: | |
| if not paths: |
That would at least check only once
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -480,6 +480,8 @@ def override_options(self): | |
| 'int', 'store', None), | ||
| 'parallel-extensions-install': ("Install list of extensions in parallel (if supported)", | ||
| None, 'store_true', False), | ||
| 'prefer-ebpythonprefix-over-pythonpath': ("Replaces PYTHONPATH with EBPYTHONPREFIX when possible", | ||
|
||
| None, 'store_true', True), | ||
| 'pre-create-installdir': ("Create installation directory before submitting build jobs", | ||
| None, 'store_true', True), | ||
| 'pretend': (("Does the build/installation in a test directory located in $HOME/easybuildinstall"), | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -694,6 +694,9 @@ def append_paths(*args, **kwargs): | |
| res = append_paths('key', ['[email protected]'], expand_relpaths=False) | ||
| self.assertEqual("append-path\tkey\t\[email protected]\n", res) | ||
|
|
||
| expected = "append-path\tEBPYTHONPREFIXES\t\t$root\nappend-path\tPYTHONPATH\t\t$root/foo\n" | ||
| res = append_paths('PYTHONPATH', ['lib/python3.12/site-packages', 'foo']) | ||
| self.assertEqual(expected, res) | ||
| else: | ||
| expected = ''.join([ | ||
| 'append_path("key", pathJoin(root, "path1"))\n', | ||
|
|
@@ -714,6 +717,10 @@ def append_paths(*args, **kwargs): | |
| res = append_paths('key', ['[email protected]'], expand_relpaths=False) | ||
| self.assertEqual('append_path("key", "[email protected]")\n', res) | ||
|
|
||
| expected = 'append_path("EBPYTHONPREFIXES", root)\nappend_path("PYTHONPATH", pathJoin(root, "foo"))\n' | ||
| res = append_paths('PYTHONPATH', ['lib/python3.12/site-packages', 'foo']) | ||
| self.assertEqual(expected, res) | ||
|
|
||
| self.assertErrorRegex(EasyBuildError, "Absolute path %s/foo passed to update_paths " | ||
| "which only expects relative paths." % self.modgen.app.installdir, | ||
| append_paths, "key2", ["bar", "%s/foo" % self.modgen.app.installdir]) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm a bit worried about the change here. Why changing
_filter_pathsand even removing the ability to pass a string (which is a breaking change leading to surprising behavior)?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The rest of the code (_update_paths) both converted the str case to a list. I simply moved this earlier, which also removes the need for _filter_paths to keep duplicate logic, since regardless it was always converted to a list immediately afterwards anyway.
You can pass a str to
pathstoappend/prepend/update_paths.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I see that our usages in this file basically make this part dead code. However someone or something else might already use this code, so I'm not sure if we should be a bit more careful here. E.g. at least error out when a string is passed instead of an iterable of strings
On the other hand: Calling this method from outside would have been wrong (it is "private") and this PR is targetting 5.x so a breaking change for simpler logic is fine.