Skip to content

Commit e04f406

Browse files
authored
add support for depends_on in views and editor from envars (#591)
Signed-off-by: vsoch <[email protected]>
1 parent 5d0ab45 commit e04f406

File tree

9 files changed

+186
-34
lines changed

9 files changed

+186
-34
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ and **Merged pull requests**. Critical items to know are:
1414
The versions coincide with releases on pip. Only major versions will be released as tags on Github.
1515

1616
## [0.0.x](https://github.com/singularityhub/singularity-hpc/tree/main) (0.0.x)
17+
- support for system modules, depends on, in views and editor envars (0.1.13)
1718
- Wrappers now supported for shell/exec/run container commands (0.1.12)
1819
- Update add to return container yaml (0.1.11)
1920
- Fixing bug with writing package file in update (0.1.1)

docs/getting_started/user-guide.rst

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -867,6 +867,7 @@ we load a view module named mpi, we always want to load a system module named "o
867867

868868
.. code-block:: console
869869
870+
$ shpc view add <view> system_modules <name1> <name2>
870871
$ shpc view add mpi system_modules openmpi mymod
871872
Wrote updated .view_module: /home/vanessa/Desktop/Code/shpc/views/mpi/.view_module
872873
@@ -891,6 +892,43 @@ Note that if you edit the files manually, you would need to edit the view.yaml A
891892
.view_module that is always updated from it.
892893

893894

895+
Add and Remove Depends On Modules to a View
896+
-------------------------------------------
897+
898+
You can add (or remove) a ``depends_on`` clause to a view, just like with system modules.
899+
The syntax is the same, however you specify a different key to add to:
900+
901+
.. code-block:: console
902+
903+
$ shpc view add <view> depends_on <name1> <name2>
904+
$ shpc view add mpi depends_on openmpi
905+
$ shpc view remove mpi depends_on openmpi
906+
907+
When you add a ``depends_on`` or ``system_modules`` to a view, what we are doing under
908+
the hood is adding a ``.view_module`` that will be loaded with the view, and it includes these
909+
extra parameters.
910+
911+
.. code-block:: console
912+
913+
views/
914+
└── mpi
915+
├── python
916+
├── view.yaml
917+
├── .view_module
918+
└── 3.11-rc.lua -> /home/vanessa/Desktop/Code/shpc/modules/python/3.11-rc/module.lua
919+
920+
Here are example contents of ``.view_module`` (this will vary depending on your module software):
921+
922+
.. code-block:: tcl
923+
924+
module load("myextraprogram")
925+
depends_on("openmpi")
926+
927+
928+
If you want any extra features added to this custom file (e.g., to support loading in a view)
929+
please open an issue for discussion.
930+
931+
894932
Delete a View
895933
-------------
896934

@@ -1018,7 +1056,8 @@ You can also open the config in the editor defined in settings at ``config_edito
10181056
$ shpc config edit
10191057
10201058
1021-
which defaults to vim.
1059+
which will first look at the environment variables ``$EDITOR`` and ``$VISUAL`` and will
1060+
fall back to the ``config_editor`` in your user settings (vim by default).
10221061

10231062
.. _getting_started-commands-show:
10241063

shpc/main/modules/templates/view_module.lua

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
--
44

55
{% for module in system_modules %}
6-
module load("{{ module }}"){% endfor %}
6+
module load("{{ module }}"){% endfor %}{% for depends in depends_on %}
7+
depends_on("{{ depends }}"){% endfor %}

shpc/main/modules/templates/view_module.tcl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,5 @@
44
# View for singularity-hpc (https://github.com/singularityhub/singularity-hpc)
55
#=====
66

7-
{% for module in system_modules %}
8-
module load {{ module }}{% endfor %}
7+
{% if system_modules %}module load {% for module in system_modules %}{{ module }} {% endfor %}{% endif %}
8+
{% if depends_on %}depends-on {% for depend in depends_on %}{{ depend }} {% endfor %}{% endif %}

shpc/main/modules/views.py

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from shpc.logger import logger
1717

1818
# Supported variables and defaults
19-
supported_view_variables = {"system_modules": []}
19+
supported_view_variables = {"system_modules": [], "depends_on": []}
2020

2121

2222
class ViewModule:
@@ -41,7 +41,8 @@ def write(self, view_dir, view_config):
4141
template = self.template.load("view_module.%s" % self.module_extension)
4242
view_module_file = os.path.join(view_dir, ".view_module")
4343
out = template.render(
44-
system_modules=view_config["view"].get("system_modules", [])
44+
system_modules=view_config["view"].get("system_modules", []),
45+
depends_on=view_config["view"].get("depends_on", []),
4546
)
4647
utils.write_file(view_module_file, out)
4748
logger.info("Wrote updated .view_module: %s" % view_module_file)
@@ -251,9 +252,16 @@ def edit(self, name):
251252

252253
def generate_view_config(self, name):
253254
"""
254-
Generate an empty view config. system_modules are not supported yet.
255+
Generate an empty view config. system_modules/depends_on are not supported yet.
255256
"""
256-
cfg = {"view": {"name": name, "modules": [], "system_modules": []}}
257+
cfg = {
258+
"view": {
259+
"name": name,
260+
"modules": [],
261+
"system_modules": [],
262+
"depends_on": [],
263+
}
264+
}
257265
self.save_config(name, cfg)
258266

259267

shpc/main/schemas.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@
193193

194194
viewProperties = {
195195
"system_modules": {"type": "array", "items": {"type": "string"}},
196+
"depends_on": {"type": "array", "items": {"type": "string"}},
196197
"modules": {"type": "array", "items": {"type": "string"}},
197198
"name": {"type": "string"},
198199
}

shpc/main/settings.py

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,38 @@ def edit(self, settings_file=None):
7676
if not settings_file or not os.path.exists(settings_file):
7777
logger.exit("%s does not exist." % settings_file)
7878

79-
# Make sure editor exists first!
80-
editor = utils.which(self.config_editor)
81-
if editor["return_code"] != 0:
79+
# Discover editor user has preferences for
80+
editor = None
81+
82+
# First try EDITOR and VISUAL envars
83+
for envar_name in ["EDITOR", "VISUAL"]:
84+
envar = os.environ.get(envar_name)
85+
editor = self._find_editor(envar)
86+
if editor is not None:
87+
break
88+
89+
# If we get here and no editor, try system default
90+
if not editor:
91+
editor = self._find_editor(self.config_editor)
92+
if not editor:
8293
logger.exit(
83-
"Editor '%s' not found! Update with shpc config set config_editor:<name>"
84-
% self.config_editor
94+
"No editors found! Update with shpc config set config_editor:<name>"
8595
)
86-
utils.run_command([self.config_editor, settings_file], stream=True)
96+
97+
utils.run_command([editor, settings_file], stream=True)
98+
99+
def _find_editor(self, path):
100+
"""
101+
Check to see that an editor exists.
102+
"""
103+
if not path:
104+
return
105+
106+
editor = utils.which(path)
107+
108+
# Only return the editor name if we find it!
109+
if editor["return_code"] == 0:
110+
return path
87111

88112
def get_settings_file(self, settings_file=None):
89113
"""

shpc/tests/test_views.py

Lines changed: 97 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -106,31 +106,37 @@ def test_views(tmp_path, module_sys, module_file, container_tech, remote):
106106
os.path.join(client.settings.module_base, "ghcr.io", "autamus")
107107
)
108108

109-
# Try adding system modules
110-
assert not view._config["view"]["system_modules"]
109+
# Before adding any attributes, this file should not exist
111110
view_module = os.path.join(view.path, ".view_module")
112111
assert not os.path.exists(view_module)
113-
view_handler.add_variable(view_name, "system_modules", "openmpi")
114-
assert os.path.exists(view_module)
115112

116-
# Make sure we have openmpi in the content
117-
content = utils.read_file(view_module)
118-
assert "openmpi" in content
113+
# Try adding valid attributes
114+
for attribute, check_content in [
115+
["system_modules", "module"],
116+
["depends_on", "depends"],
117+
]:
118+
assert not view._config["view"][attribute]
119+
view_handler.add_variable(view_name, attribute, "openmpi")
120+
assert os.path.exists(view_module)
119121

120-
# Reload the view config file
121-
view.reload()
122-
assert "openmpi" in view._config["view"]["system_modules"]
122+
# Make sure we have openmpi in the content
123+
content = utils.read_file(view_module)
124+
assert "openmpi" in content and check_content in content
123125

124-
# We can't add unknown variables
125-
with pytest.raises(SystemExit):
126-
view_handler.add_variable(view_name, "system-modules", [1, 2, 3])
126+
# Reload the view config file
127+
view.reload()
128+
assert "openmpi" in view._config["view"][attribute]
127129

128-
# Try removing now
129-
view_handler.remove_variable(view_name, "system_modules", "openmpi")
130-
view.reload()
131-
assert not view._config["view"]["system_modules"]
132-
content = utils.read_file(view_module)
133-
assert "openmpi" not in content
130+
# We can't add unknown variables
131+
with pytest.raises(SystemExit):
132+
view_handler.add_variable(view_name, attribute.replace("_", "-"), [1, 2, 3])
133+
134+
# Try removing now
135+
view_handler.remove_variable(view_name, attribute, "openmpi")
136+
view.reload()
137+
assert not view._config["view"][attribute]
138+
content = utils.read_file(view_module)
139+
assert "openmpi" not in content and check_content not in content
134140

135141
# Ensure we can uninstall
136142
view_handler.delete(view_name, force=True)
@@ -141,3 +147,75 @@ def test_views(tmp_path, module_sys, module_file, container_tech, remote):
141147
view_client.create_from_file(
142148
view_name, views_config, settings_file=client.settings.settings_file, force=True
143149
)
150+
151+
152+
@pytest.mark.parametrize(
153+
"module_sys,module_file,container_tech,remote",
154+
[
155+
("lmod", "module.lua", "singularity", True),
156+
("lmod", "module.lua", "podman", True),
157+
("tcl", "module.tcl", "singularity", True),
158+
("tcl", "module.tcl", "podman", True),
159+
("lmod", "module.lua", "singularity", False),
160+
("lmod", "module.lua", "podman", False),
161+
("tcl", "module.tcl", "singularity", False),
162+
("tcl", "module.tcl", "podman", False),
163+
],
164+
)
165+
def test_view_components(tmp_path, module_sys, module_file, container_tech, remote):
166+
"""
167+
Test view components
168+
"""
169+
client = init_client(str(tmp_path), module_sys, container_tech, remote=remote)
170+
171+
# Create the view handler based on the client settings file
172+
view_handler = views.ViewsHandler(
173+
settings_file=client.settings.settings_file, module_sys=module_sys
174+
)
175+
176+
# A view name
177+
view_name = "mpi"
178+
assert view_name not in client.views
179+
180+
# Create the view!
181+
view_handler.create(view_name)
182+
client.detect_views()
183+
assert view_name in client.views
184+
185+
# Before adding any attributes, this file should not exist
186+
view = client.views[view_name]
187+
view_module = os.path.join(view.path, ".view_module")
188+
assert not os.path.exists(view_module)
189+
190+
# Try adding valid attributes
191+
for attribute, check_content in [
192+
["system_modules", "module"],
193+
["depends_on", "depends"],
194+
]:
195+
assert not view._config["view"][attribute]
196+
view_handler.add_variable(view_name, attribute, "openmpi")
197+
assert os.path.exists(view_module)
198+
199+
# Make sure we have openmpi in the content
200+
content = utils.read_file(view_module)
201+
print(content)
202+
assert "openmpi" in content and check_content in content
203+
204+
# Reload the view config file
205+
view.reload()
206+
assert "openmpi" in view._config["view"][attribute]
207+
208+
# We can't add unknown variables
209+
with pytest.raises(SystemExit):
210+
view_handler.add_variable(view_name, attribute.replace("_", "-"), [1, 2, 3])
211+
212+
# Try removing now
213+
view_handler.remove_variable(view_name, attribute, "openmpi")
214+
view.reload()
215+
assert not view._config["view"][attribute]
216+
content = utils.read_file(view_module)
217+
assert "openmpi" not in content and check_content not in content
218+
219+
# Ensure we can uninstall
220+
view_handler.delete(view_name, force=True)
221+
assert not os.path.exists(view.path)

shpc/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
__copyright__ = "Copyright 2021-2022, Vanessa Sochat"
33
__license__ = "MPL 2.0"
44

5-
__version__ = "0.1.12"
5+
__version__ = "0.1.13"
66
AUTHOR = "Vanessa Sochat"
77
88
NAME = "singularity-hpc"

0 commit comments

Comments
 (0)