Repository: spack/spack

Issue:
Modules that autoload other modules should do it quietly.
Loading a module (e.g. `module load py-flake8`) should not generate extraneous output otherwise modules become very difficult to use inside scripts because the `lmod` output has to be filtered out of the real output.

Right now, this happens:

```
[hartzelg@rpbuchop001 spack-20161212]$ module load py-flake8
Autoloading py-mccabe/0.5.2
Autoloading python/2.7.12
Autoloading bzip2/1.0.6
Autoloading ncurses/6.0
Autoloading zlib/1.2.8
Autoloading openssl/1.0.2j
Autoloading sqlite/3.8.5
Autoloading readline/6.3
Autoloading py-enum34/1.1.6
Autoloading py-pyflakes/1.2.3
Autoloading py-pycodestyle/2.0.0
Autoloading py-configparser/3.5.0
[hartzelg@rpbuchop001 spack-20161212]$
```

Loading a module that does not contain any autoloads is happily silent.

```
[hartzelg@rpbuchop001 spack-20161212]$ module load sqlite
[hartzelg@rpbuchop001 spack-20161212]$
```

The "Autoloading..." message comes from bits like this in the generated modulefiles.

```
if not isloaded("py-enum34/1.1.6") then
    LmodMessage("Autoloading py-enum34/1.1.6")
    load("py-enum34/1.1.6")
end
```

This (for Lmod) is in turn generated by this bit in `lib/spack/spack/modules.py`.

```
    autoload_format = ('if not isloaded("{module_file}") then\n'
                       '    LmodMessage("Autoloading {module_file}")\n'
                       '    load("{module_file}")\n'
                       'end\n\n')
```

I'd like to remove the message from both the tcl and Lmod autoload formats.

Is there someone that feels strongly that autoloaded modules should be noisy?




Discussion:
@alalazo 
That was added on purpose. I think people would be quite surprised when encountering a situation like:
```console
$ module purge
$ module load a
$ module list
a b c d e f g h ...
```
What about adding a way to opt-out of the verbose output, instead of removing it?
Hmmm.   I'm a people and I went out of my way to set up the autoloading bit.  *I* would be surprised if the things were *not* loaded.

I'm old-school and prefer that things that succeed do so silently, otherwise using them as components in bigger systems is tricky.

Left to my own devices (here's where you say *"PR welcome..."*) I'd make the verbosity *opt-in* so that it matches the usual behavior of `module load`.
> Hmmm. I'm a people and I went out of my way to set up the auto-loading bit. I would be surprised if the things were not loaded.

... because you were the same person that edited the `spack` configuration file for modules, and you knew what to expect? As far as I have seen TCL or LUA modules are not often written with logic that loads other modules, so I followed the principle of least astonishment there.

Anyhow, I don't mind if we want to have non-verbose as the default behavior, as long as the verbose choice remains.
To clarify, a verbose/non-verbose flag would be added to `modules.yaml`, correct? I think that would make everyone happy.
> To clarify, a verbose/non-verbose flag would be added to modules.yaml, correct? I think that would make everyone happy.

Yes, that was basically my proposal :smile: 
If it's a flag in the `modules.yaml` file that changes the generated lua modules, I don't care what the default is either (I was worried about how I was going to get all my users to set the MODULES_SILENT_AUTOLOAD environment variable...).

So something like this:

```
modules:
  enable::
    - lmod
  lmod:
    [...]
    hash_length: 0
    blacklist_implicits: False
    verbose_autoload: False
    [...]
```

and then just conditionalize the template based on the value from the configuration (pardon my pidgin python, the interpreter will help me get it right later...)?

```python
        configuration = _module_config.get(self.name, {})
        if configuration.get('verbose_autoload'):
```

@hartzell I use `environment-modules`, not `lmod`, hence no transitive loading in modules.  I've been pretty happy using `spack module loads -r` to recursive generate lists of modules to load.

@citibeth -- *thanks*, I'll take that out for a spin.  Do you know (i'll try it when I get a tuit) whether `spack module loads -r` gets the `py-setuptools` dependency for `py-flake` correct?



Resolving patch:
diff --git a/lib/spack/spack/modules.py b/lib/spack/spack/modules.py
--- a/lib/spack/spack/modules.py
+++ b/lib/spack/spack/modules.py
@@ -428,13 +428,20 @@ def header(self):
     def module_specific_content(self, configuration):
         return tuple()
 
+    # Subclasses can return a fragment of module code that prints out
+    # a warning that modules are being autoloaded.
+    def autoload_warner(self):
+        return ''
+
     def autoload(self, spec):
         if not isinstance(spec, str):
             m = type(self)(spec)
             module_file = m.use_name
         else:
             module_file = spec
-        return self.autoload_format.format(module_file=module_file)
+        return self.autoload_format.format(
+            module_file=module_file,
+            warner=self.autoload_warner().format(module_file=module_file))
 
     def prerequisite(self, spec):
         m = type(self)(spec)
@@ -476,6 +483,10 @@ def remove(self):
                 # removedirs throws OSError on first non-empty directory found
                 pass
 
+    def verbose_autoload(self):
+        configuration = _module_config.get(self.name, {})
+        return configuration.get('verbose_autoload', True)
+
 
 class Dotkit(EnvModule):
     name = 'dotkit'
@@ -527,8 +538,13 @@ class TclModule(EnvModule):
     path = canonicalize_path(
         _roots.get(name, join_path(spack.share_path, 'modules')))
 
+    def autoload_warner(self):
+        if self.verbose_autoload():
+            return 'puts stderr "Autoloading {module_file}"\n'
+        return ''
+
     autoload_format = ('if ![ is-loaded {module_file} ] {{\n'
-                       '    puts stderr "Autoloading {module_file}"\n'
+                       '    {warner}'
                        '    module load {module_file}\n'
                        '}}\n\n')
 
@@ -655,8 +671,13 @@ class LmodModule(EnvModule):
         UnsetEnv: 'unsetenv("{name}")\n'
     }
 
+    def autoload_warner(self):
+        if self.verbose_autoload():
+            return 'LmodMessage("Autoloading {module_file}")\n'
+        return ''
+
     autoload_format = ('if not isloaded("{module_file}") then\n'
-                       '    LmodMessage("Autoloading {module_file}")\n'
+                       '    {warner}'
                        '    load("{module_file}")\n'
                        'end\n\n')
 


Test patch: