Repository: sdss/tree

Issue:
make module dependencies optional
Hi @havok2063 

I ran some tests installing the SDSS software framework via pip install sdss-access which then installs sdss-tree, and sdsstools (without modules).  I then ran setup_tree.py by following https://sdss-tree.readthedocs.io/en/latest/tree_setup.html#creating-environment-symlinks.  This worked great, except the resulting modulefiles include module load dependencies for sdsstools and sdss_access, which is broken since those dependencies were pip installed and do not have modulefiles, and if they did the pythonpath would over-ride the pip installed paths, so this usecase is broken.

Can we remove the module file dependencies so that tree does not look for module files for sdss_access or sdsstools or do you have another way to fix this?

Thanks,
Joel

Comments:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 5f964f9..add00c6 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -6,6 +6,11 @@ Change Log
 
 This document records the main changes to the tree code.
 
+3.1.5 (unreleased)
+------------------
+- Added new config for IPL-2
+- Make ``sdsstool`` and ``sdss_access`` module dependencies optional in ``setup_tree``
+
 3.1.4 (2022-11-29)
 ------------------
 - Adding new config for IPL-1
diff --git a/bin/setup_tree.py b/bin/setup_tree.py
index c4d8bc5..aae7152 100755
--- a/bin/setup_tree.py
+++ b/bin/setup_tree.py
@@ -225,7 +225,7 @@ def check_sas_base_dir(root=None):
     os.environ['SAS_BASE_DIR'] = sasbasedir
 
 
-def write_header(term='bash', tree_dir=None, name=None):
+def write_header(term='bash', tree_dir=None, name=None, add_mod_deps=False):
     ''' Write proper file header in a given shell format
 
     Parameters:
@@ -246,6 +246,15 @@ def write_header(term='bash', tree_dir=None, name=None):
     base = 'export' if term == 'bash' else 'setenv'
     sep = '=' if term == 'bash' else ' '
 
+    # add the optional depdendecies
+    deps = """
+module load sdsstools
+prereq sdsstools
+module load sdss_access
+prereq sdss_access
+    """ if add_mod_deps else ""
+
+
     if term != 'modules':
         hdr = """# Set up tree/{0} for {1}
 {2} TREE_DIR{4}{3}
@@ -264,10 +273,7 @@ set product tree
 set version {1}
 conflict $product
 
-module load sdsstools
-prereq sdsstools
-module load sdss_access
-prereq sdss_access
+{2}
 
 module-whatis "Sets up $product/$version in your environment"
 
@@ -277,7 +283,7 @@ setenv [string toupper $product]_VER $version
 prepend-path PATH $PRODUCT_DIR/bin
 prepend-path PYTHONPATH $PRODUCT_DIR/python
 
-                """.format(product_dir, name)
+                """.format(product_dir, name, deps)
 
     return hdr.strip()
 
@@ -288,7 +294,8 @@ def write_version(name):
     return modules_version
 
 
-def write_file(environ, term='bash', out_dir=None, tree_dir=None, default=None):
+def write_file(environ, term='bash', out_dir=None, tree_dir=None,
+               default=None, add_mod_deps=None):
     ''' Write a tree environment file
 
     Loops over the tree environ and writes them out to a bash, tsch, or
@@ -310,7 +317,7 @@ def write_file(environ, term='bash', out_dir=None, tree_dir=None, default=None):
 
     # get the proper name, header and file extension
     name = environ['default']['name']
-    header = write_header(term=term, name=name, tree_dir=tree_dir)
+    header = write_header(term=term, name=name, tree_dir=tree_dir, add_mod_deps=add_mod_deps)
     exts = {'bash': '.sh', 'tsch': '.csh', 'modules': '.module'}
     ext = exts[term]
 
@@ -498,7 +505,8 @@ def get_parser():
                         help='Custom output path to copy environment files')
     parser.add_argument('-f', '--force', action='store_true', dest='force',
                         help='Force overwrite of existing modulefiles', default=False)
-
+    parser.add_argument('-a', '--add-module-deps', action='store_true', dest='add_mod_deps',
+                        help='Add the sdsstools and sdss-access module prereq dependencies', default=False)
     return parser
 
 
@@ -548,7 +556,7 @@ def main(opts):
                 continue
             create_env(tree.environ, mirror=opts.mirror)
         else:
-            write_file(tree.environ, term='modules', out_dir=etcdir, tree_dir=opts.treedir, default=opts.default)
+            write_file(tree.environ, term='modules', out_dir=etcdir, tree_dir=opts.treedir, default=opts.default, add_mod_deps=opts.add_mod_deps)
             write_file(tree.environ, term='bash', out_dir=etcdir, tree_dir=opts.treedir)
             write_file(tree.environ, term='tsch', out_dir=etcdir, tree_dir=opts.treedir)
 
diff --git a/docs/sphinx/config.rst b/docs/sphinx/config.rst
index 943dd46..90d828b 100644
--- a/docs/sphinx/config.rst
+++ b/docs/sphinx/config.rst
@@ -24,6 +24,7 @@ configurations are the following:
 * :ref:`DR8 <dr8>`
 * :ref:`sdss5 <sdss5>` - the latest working configuration for SDSS-V
 * :ref:`ipl1 <ipl1>` - the IPL-1 configuration for SDSS-V
+* :ref:`ipl2 <ipl2>` - the IPL-2 configuration for SDSS-V
 
 .. _tree_evolve:
 
diff --git a/docs/sphinx/tree_envs.rst b/docs/sphinx/tree_envs.rst
index 1affa04..a6c1412 100644
--- a/docs/sphinx/tree_envs.rst
+++ b/docs/sphinx/tree_envs.rst
@@ -172,4 +172,16 @@ This is the configuration for IPL-1
 .. datamodel:: tree.tree:Tree
    :prog: ipl-1
    :title: ipl-1
+   :remove-sasbase:
+
+.. _ipl2:
+
+IPL-2
+-----
+
+This is the configuration for IPL-2
+
+.. datamodel:: tree.tree:Tree
+   :prog: ipl-2
+   :title: ipl-2
    :remove-sasbase:
\ No newline at end of file
diff --git a/setup.cfg b/setup.cfg
index d2815f1..56581ef 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -72,13 +72,14 @@ dev =
 	wheel>=0.33.6
 
 docs =
-	Sphinx>=2.1.0
+	Sphinx>=3.0.0
 	sphinx_bootstrap_theme>=0.4.12
 	recommonmark>=0.6
 	sphinx-argparse>=0.2.5
 	sphinx-issues>=1.2.0
 	importlib_metadata>=1.6.0
 	jinja2<3.1
+	six>=1.14
 
 [isort]
 line_length = 100


Test patch:
diff --git a/tests/test_setup.py b/tests/test_setup.py
index bfa1033..ba0402f 100644
--- a/tests/test_setup.py
+++ b/tests/test_setup.py
@@ -129,6 +129,30 @@ def test_create_files(tree, config):
     assert os.path.exists(os.path.join(moduledir, config))
 
 
+def test_no_mod_deps(tree):
+    """ test that the module deps are not in module file """
+    etcdir = os.path.join(os.getenv('TREE_DIR'), 'etc')
+    stdout = run_cmd(args=[])
+    files = glob.glob(os.path.join(etcdir, 'dr18.module'))
+    assert os.path.exists(os.path.join(etcdir, 'dr18.module'))
+    with open(files[0], 'r') as f:
+        data = f.read()
+        assert "module load sdsstools" not in data
+        assert "prereq sdss_access" not in data
+
+
+def test_add_mod_deps(tree):
+    """ test that the module deps are not in module file """
+    etcdir = os.path.join(os.getenv('TREE_DIR'), 'etc')
+    stdout = run_cmd(args=['-a'])
+    files = glob.glob(os.path.join(etcdir, 'dr18.module'))
+    assert os.path.exists(os.path.join(etcdir, 'dr18.module'))
+    with open(files[0], 'r') as f:
+        data = f.read()
+        assert "module load sdsstools" in data
+        assert "prereq sdss_access" in data
+
+
 @pytest.fixture()
 def resetmod(monkeypatch):
     mdir = os.environ.get('MODULES_DIR')