We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
spack module (tcl|lcov|dotkit) loads: does not work with upstream packages
`spack module * loads` does not work with upstream packages.

### Steps to reproduce the issue

```console
$ spack module <module-type> loads <package_in_upstream>
```

### Error Message

```console
==> Error: 'module' object has no attribute 'upstream_module'
```

### Information on your system

I configured `upstreams.yaml` like this

```yaml
upstreams:
  myupstream:
    install_tree: <UPSTREAM_SPACK_ROOT>/opt/spack
    modules:
      tcl: <UPSTREAM_SPACK_ROOT>/share/spack/modules
```
fixes spack module loads for upstream packages
Fixes #12062

It seems that a non-existing function was used. I replaced it with the one used also by the `find` sub-command.

@scheibelp, it seems that last change to this section was from you, I tried tracking if this function was existing before, but with no luck. I mentioned you just because you may be able to check quickly if this is the right fix.

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `lib/spack/spack/cmd/modules/__init__.py`
Content:
```
1 # Copyright 2013-2019 Lawrence Livermore National Security, LLC and other
2 # Spack Project Developers. See the top-level COPYRIGHT file for details.
3 #
4 # SPDX-License-Identifier: (Apache-2.0 OR MIT)
5 
6 """Implementation details of the ``spack module`` command."""
7 
8 import collections
9 import os.path
10 import shutil
11 import sys
12 
13 from llnl.util import filesystem, tty
14 
15 import spack.cmd
16 import spack.modules
17 import spack.repo
18 import spack.modules.common
19 
20 import spack.cmd.common.arguments as arguments
21 
22 description = "manipulate module files"
23 section = "environment"
24 level = "short"
25 
26 
27 def setup_parser(subparser):
28     sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='subparser_name')
29 
30     refresh_parser = sp.add_parser('refresh', help='regenerate module files')
31     refresh_parser.add_argument(
32         '--delete-tree',
33         help='delete the module file tree before refresh',
34         action='store_true'
35     )
36     refresh_parser.add_argument(
37         '--upstream-modules',
38         help='generate modules for packages installed upstream',
39         action='store_true'
40     )
41     arguments.add_common_arguments(
42         refresh_parser, ['constraint', 'yes_to_all']
43     )
44 
45     find_parser = sp.add_parser('find', help='find module files for packages')
46     find_parser.add_argument(
47         '--full-path',
48         help='display full path to module file',
49         action='store_true'
50     )
51     arguments.add_common_arguments(
52         find_parser, ['constraint', 'recurse_dependencies']
53     )
54 
55     rm_parser = sp.add_parser('rm', help='remove module files')
56     arguments.add_common_arguments(
57         rm_parser, ['constraint', 'yes_to_all']
58     )
59 
60     loads_parser = sp.add_parser(
61         'loads',
62         help='prompt the list of modules associated with a constraint'
63     )
64     add_loads_arguments(loads_parser)
65     arguments.add_common_arguments(loads_parser, ['constraint'])
66 
67     return sp
68 
69 
70 def add_loads_arguments(subparser):
71     subparser.add_argument(
72         '--input-only', action='store_false', dest='shell',
73         help='generate input for module command (instead of a shell script)'
74     )
75     subparser.add_argument(
76         '-p', '--prefix', dest='prefix', default='',
77         help='prepend to module names when issuing module load commands'
78     )
79     subparser.add_argument(
80         '-x', '--exclude', dest='exclude', action='append', default=[],
81         help="exclude package from output; may be specified multiple times"
82     )
83     arguments.add_common_arguments(
84         subparser, ['recurse_dependencies']
85     )
86 
87 
88 class MultipleSpecsMatch(Exception):
89     """Raised when multiple specs match a constraint, in a context where
90     this is not allowed.
91     """
92 
93 
94 class NoSpecMatches(Exception):
95     """Raised when no spec matches a constraint, in a context where
96     this is not allowed.
97     """
98 
99 
100 def one_spec_or_raise(specs):
101     """Ensures exactly one spec has been selected, or raises the appropriate
102     exception.
103     """
104     # Ensure a single spec matches the constraint
105     if len(specs) == 0:
106         raise NoSpecMatches()
107     if len(specs) > 1:
108         raise MultipleSpecsMatch()
109 
110     # Get the spec and module type
111     return specs[0]
112 
113 
114 def loads(module_type, specs, args, out=sys.stdout):
115     """Prompt the list of modules associated with a list of specs"""
116 
117     # Get a comprehensive list of specs
118     if args.recurse_dependencies:
119         specs_from_user_constraint = specs[:]
120         specs = []
121         # FIXME : during module file creation nodes seem to be visited
122         # FIXME : multiple times even if cover='nodes' is given. This
123         # FIXME : work around permits to get a unique list of spec anyhow.
124         # FIXME : (same problem as in spack/modules.py)
125         seen = set()
126         seen_add = seen.add
127         for spec in specs_from_user_constraint:
128             specs.extend(
129                 [item for item in spec.traverse(order='post', cover='nodes')
130                  if not (item in seen or seen_add(item))]
131             )
132 
133     module_cls = spack.modules.module_types[module_type]
134     modules = list()
135     for spec in specs:
136         if os.path.exists(module_cls(spec).layout.filename):
137             modules.append((spec, module_cls(spec).layout.use_name))
138         elif spec.package.installed_upstream:
139             tty.debug("Using upstream module for {0}".format(spec))
140             module = spack.modules.common.upstream_module(spec, module_type)
141             modules.append((spec, module.use_name))
142 
143     module_commands = {
144         'tcl': 'module load ',
145         'lmod': 'module load ',
146         'dotkit': 'use '
147     }
148 
149     d = {
150         'command': '' if not args.shell else module_commands[module_type],
151         'prefix': args.prefix
152     }
153 
154     exclude_set = set(args.exclude)
155     prompt_template = '{comment}{exclude}{command}{prefix}{name}'
156     for spec, mod in modules:
157         d['exclude'] = '## ' if spec.name in exclude_set else ''
158         d['comment'] = '' if not args.shell else '# {0}\n'.format(
159             spec.format())
160         d['name'] = mod
161         out.write(prompt_template.format(**d))
162         out.write('\n')
163 
164 
165 def find(module_type, specs, args):
166     """Retrieve paths or use names of module files"""
167 
168     single_spec = one_spec_or_raise(specs)
169 
170     if args.recurse_dependencies:
171         specs_to_retrieve = list(
172             single_spec.traverse(order='post', cover='nodes',
173                                  deptype=('link', 'run')))
174     else:
175         specs_to_retrieve = [single_spec]
176 
177     try:
178         modules = [spack.modules.common.get_module(module_type, spec,
179                                                    args.full_path)
180                    for spec in specs_to_retrieve]
181     except spack.modules.common.ModuleNotFoundError as e:
182         tty.die(e.message)
183     print(' '.join(modules))
184 
185 
186 def rm(module_type, specs, args):
187     """Deletes the module files associated with every spec in specs, for every
188     module type in module types.
189     """
190 
191     module_cls = spack.modules.module_types[module_type]
192     module_exist = lambda x: os.path.exists(module_cls(x).layout.filename)
193 
194     specs_with_modules = [spec for spec in specs if module_exist(spec)]
195 
196     modules = [module_cls(spec) for spec in specs_with_modules]
197 
198     if not modules:
199         tty.die('No module file matches your query')
200 
201     # Ask for confirmation
202     if not args.yes_to_all:
203         msg = 'You are about to remove {0} module files for:\n'
204         tty.msg(msg.format(module_type))
205         spack.cmd.display_specs(specs_with_modules, long=True)
206         print('')
207         answer = tty.get_yes_or_no('Do you want to proceed?')
208         if not answer:
209             tty.die('Will not remove any module files')
210 
211     # Remove the module files
212     for s in modules:
213         s.remove()
214 
215 
216 def refresh(module_type, specs, args):
217     """Regenerates the module files for every spec in specs and every module
218     type in module types.
219     """
220 
221     # Prompt a message to the user about what is going to change
222     if not specs:
223         tty.msg('No package matches your query')
224         return
225 
226     if not args.upstream_modules:
227         specs = list(s for s in specs if not s.package.installed_upstream)
228 
229     if not args.yes_to_all:
230         msg = 'You are about to regenerate {types} module files for:\n'
231         tty.msg(msg.format(types=module_type))
232         spack.cmd.display_specs(specs, long=True)
233         print('')
234         answer = tty.get_yes_or_no('Do you want to proceed?')
235         if not answer:
236             tty.die('Module file regeneration aborted.')
237 
238     # Cycle over the module types and regenerate module files
239 
240     cls = spack.modules.module_types[module_type]
241 
242     # Skip unknown packages.
243     writers = [
244         cls(spec) for spec in specs
245         if spack.repo.path.exists(spec.name)]
246 
247     # Filter blacklisted packages early
248     writers = [x for x in writers if not x.conf.blacklisted]
249 
250     # Detect name clashes in module files
251     file2writer = collections.defaultdict(list)
252     for item in writers:
253         file2writer[item.layout.filename].append(item)
254 
255     if len(file2writer) != len(writers):
256         message = 'Name clashes detected in module files:\n'
257         for filename, writer_list in file2writer.items():
258             if len(writer_list) > 1:
259                 message += '\nfile: {0}\n'.format(filename)
260                 for x in writer_list:
261                     message += 'spec: {0}\n'.format(x.spec.format())
262         tty.error(message)
263         tty.error('Operation aborted')
264         raise SystemExit(1)
265 
266     if len(writers) == 0:
267         msg = 'Nothing to be done for {0} module files.'
268         tty.msg(msg.format(module_type))
269         return
270 
271     # If we arrived here we have at least one writer
272     module_type_root = writers[0].layout.dirname()
273     spack.modules.common.generate_module_index(module_type_root, writers)
274     # Proceed regenerating module files
275     tty.msg('Regenerating {name} module files'.format(name=module_type))
276     if os.path.isdir(module_type_root) and args.delete_tree:
277         shutil.rmtree(module_type_root, ignore_errors=False)
278     filesystem.mkdirp(module_type_root)
279     for x in writers:
280         try:
281             x.write(overwrite=True)
282         except Exception as e:
283             tty.debug(e)
284             msg = 'Could not write module file [{0}]'
285             tty.warn(msg.format(x.layout.filename))
286             tty.warn('\t--> {0} <--'.format(str(e)))
287 
288 
289 #: Dictionary populated with the list of sub-commands.
290 #: Each sub-command must be callable and accept 3 arguments:
291 #:
292 #:   - module_type: the type of module it refers to
293 #:   - specs : the list of specs to be processed
294 #:   - args : namespace containing the parsed command line arguments
295 callbacks = {
296     'refresh': refresh,
297     'rm': rm,
298     'find': find,
299     'loads': loads
300 }
301 
302 
303 def modules_cmd(parser, args, module_type, callbacks=callbacks):
304 
305     # Qualifiers to be used when querying the db for specs
306     constraint_qualifiers = {
307         'refresh': {
308             'installed': True,
309             'known': True
310         },
311     }
312     query_args = constraint_qualifiers.get(args.subparser_name, {})
313 
314     # Get the specs that match the query from the DB
315     specs = args.specs(**query_args)
316 
317     try:
318 
319         callbacks[args.subparser_name](module_type, specs, args)
320 
321     except MultipleSpecsMatch:
322         msg = "the constraint '{query}' matches multiple packages:\n"
323         for s in specs:
324             spec_fmt = '{hash:7} {name}{@version}{%compiler}'
325             spec_fmt += '{compiler_flags}{variants}{arch=architecture}'
326             msg += '\t' + s.cformat(spec_fmt) + '\n'
327         tty.error(msg.format(query=args.constraint))
328         tty.die('In this context exactly **one** match is needed: please specify your constraints better.')  # NOQA: ignore=E501
329 
330     except NoSpecMatches:
331         msg = "the constraint '{query}' matches no package."
332         tty.error(msg.format(query=args.constraint))
333         tty.die('In this context exactly **one** match is needed: please specify your constraints better.')  # NOQA: ignore=E501
334
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/lib/spack/spack/cmd/modules/__init__.py b/lib/spack/spack/cmd/modules/__init__.py
--- a/lib/spack/spack/cmd/modules/__init__.py
+++ b/lib/spack/spack/cmd/modules/__init__.py
@@ -130,15 +130,9 @@
                  if not (item in seen or seen_add(item))]
             )
 
-    module_cls = spack.modules.module_types[module_type]
-    modules = list()
-    for spec in specs:
-        if os.path.exists(module_cls(spec).layout.filename):
-            modules.append((spec, module_cls(spec).layout.use_name))
-        elif spec.package.installed_upstream:
-            tty.debug("Using upstream module for {0}".format(spec))
-            module = spack.modules.common.upstream_module(spec, module_type)
-            modules.append((spec, module.use_name))
+    modules = list(
+        (spec, spack.modules.common.get_module(module_type, spec, False))
+        for spec in specs)
 
     module_commands = {
         'tcl': 'module load ',

{"golden_diff": "diff --git a/lib/spack/spack/cmd/modules/__init__.py b/lib/spack/spack/cmd/modules/__init__.py\n--- a/lib/spack/spack/cmd/modules/__init__.py\n+++ b/lib/spack/spack/cmd/modules/__init__.py\n@@ -130,15 +130,9 @@\n                  if not (item in seen or seen_add(item))]\n             )\n \n-    module_cls = spack.modules.module_types[module_type]\n-    modules = list()\n-    for spec in specs:\n-        if os.path.exists(module_cls(spec).layout.filename):\n-            modules.append((spec, module_cls(spec).layout.use_name))\n-        elif spec.package.installed_upstream:\n-            tty.debug(\"Using upstream module for {0}\".format(spec))\n-            module = spack.modules.common.upstream_module(spec, module_type)\n-            modules.append((spec, module.use_name))\n+    modules = list(\n+        (spec, spack.modules.common.get_module(module_type, spec, False))\n+        for spec in specs)\n \n     module_commands = {\n         'tcl': 'module load ',\n", "issue": "spack module (tcl|lcov|dotkit) loads: does not work with upstream packages\n`spack module * loads` does not work with upstream packages.\r\n\r\n### Steps to reproduce the issue\r\n\r\n```console\r\n$ spack module <module-type> loads <package_in_upstream>\r\n```\r\n\r\n### Error Message\r\n\r\n```console\r\n==> Error: 'module' object has no attribute 'upstream_module'\r\n```\r\n\r\n### Information on your system\r\n\r\nI configured `upstreams.yaml` like this\r\n\r\n```yaml\r\nupstreams:\r\n  myupstream:\r\n    install_tree: <UPSTREAM_SPACK_ROOT>/opt/spack\r\n    modules:\r\n      tcl: <UPSTREAM_SPACK_ROOT>/share/spack/modules\r\n```\nfixes spack module loads for upstream packages\nFixes #12062\r\n\r\nIt seems that a non-existing function was used. I replaced it with the one used also by the `find` sub-command.\r\n\r\n@scheibelp, it seems that last change to this section was from you, I tried tracking if this function was existing before, but with no luck. I mentioned you just because you may be able to check quickly if this is the right fix.\n", "before_files": [{"content": "# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other\n# Spack Project Developers. See the top-level COPYRIGHT file for details.\n#\n# SPDX-License-Identifier: (Apache-2.0 OR MIT)\n\n\"\"\"Implementation details of the ``spack module`` command.\"\"\"\n\nimport collections\nimport os.path\nimport shutil\nimport sys\n\nfrom llnl.util import filesystem, tty\n\nimport spack.cmd\nimport spack.modules\nimport spack.repo\nimport spack.modules.common\n\nimport spack.cmd.common.arguments as arguments\n\ndescription = \"manipulate module files\"\nsection = \"environment\"\nlevel = \"short\"\n\n\ndef setup_parser(subparser):\n    sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='subparser_name')\n\n    refresh_parser = sp.add_parser('refresh', help='regenerate module files')\n    refresh_parser.add_argument(\n        '--delete-tree',\n        help='delete the module file tree before refresh',\n        action='store_true'\n    )\n    refresh_parser.add_argument(\n        '--upstream-modules',\n        help='generate modules for packages installed upstream',\n        action='store_true'\n    )\n    arguments.add_common_arguments(\n        refresh_parser, ['constraint', 'yes_to_all']\n    )\n\n    find_parser = sp.add_parser('find', help='find module files for packages')\n    find_parser.add_argument(\n        '--full-path',\n        help='display full path to module file',\n        action='store_true'\n    )\n    arguments.add_common_arguments(\n        find_parser, ['constraint', 'recurse_dependencies']\n    )\n\n    rm_parser = sp.add_parser('rm', help='remove module files')\n    arguments.add_common_arguments(\n        rm_parser, ['constraint', 'yes_to_all']\n    )\n\n    loads_parser = sp.add_parser(\n        'loads',\n        help='prompt the list of modules associated with a constraint'\n    )\n    add_loads_arguments(loads_parser)\n    arguments.add_common_arguments(loads_parser, ['constraint'])\n\n    return sp\n\n\ndef add_loads_arguments(subparser):\n    subparser.add_argument(\n        '--input-only', action='store_false', dest='shell',\n        help='generate input for module command (instead of a shell script)'\n    )\n    subparser.add_argument(\n        '-p', '--prefix', dest='prefix', default='',\n        help='prepend to module names when issuing module load commands'\n    )\n    subparser.add_argument(\n        '-x', '--exclude', dest='exclude', action='append', default=[],\n        help=\"exclude package from output; may be specified multiple times\"\n    )\n    arguments.add_common_arguments(\n        subparser, ['recurse_dependencies']\n    )\n\n\nclass MultipleSpecsMatch(Exception):\n    \"\"\"Raised when multiple specs match a constraint, in a context where\n    this is not allowed.\n    \"\"\"\n\n\nclass NoSpecMatches(Exception):\n    \"\"\"Raised when no spec matches a constraint, in a context where\n    this is not allowed.\n    \"\"\"\n\n\ndef one_spec_or_raise(specs):\n    \"\"\"Ensures exactly one spec has been selected, or raises the appropriate\n    exception.\n    \"\"\"\n    # Ensure a single spec matches the constraint\n    if len(specs) == 0:\n        raise NoSpecMatches()\n    if len(specs) > 1:\n        raise MultipleSpecsMatch()\n\n    # Get the spec and module type\n    return specs[0]\n\n\ndef loads(module_type, specs, args, out=sys.stdout):\n    \"\"\"Prompt the list of modules associated with a list of specs\"\"\"\n\n    # Get a comprehensive list of specs\n    if args.recurse_dependencies:\n        specs_from_user_constraint = specs[:]\n        specs = []\n        # FIXME : during module file creation nodes seem to be visited\n        # FIXME : multiple times even if cover='nodes' is given. This\n        # FIXME : work around permits to get a unique list of spec anyhow.\n        # FIXME : (same problem as in spack/modules.py)\n        seen = set()\n        seen_add = seen.add\n        for spec in specs_from_user_constraint:\n            specs.extend(\n                [item for item in spec.traverse(order='post', cover='nodes')\n                 if not (item in seen or seen_add(item))]\n            )\n\n    module_cls = spack.modules.module_types[module_type]\n    modules = list()\n    for spec in specs:\n        if os.path.exists(module_cls(spec).layout.filename):\n            modules.append((spec, module_cls(spec).layout.use_name))\n        elif spec.package.installed_upstream:\n            tty.debug(\"Using upstream module for {0}\".format(spec))\n            module = spack.modules.common.upstream_module(spec, module_type)\n            modules.append((spec, module.use_name))\n\n    module_commands = {\n        'tcl': 'module load ',\n        'lmod': 'module load ',\n        'dotkit': 'use '\n    }\n\n    d = {\n        'command': '' if not args.shell else module_commands[module_type],\n        'prefix': args.prefix\n    }\n\n    exclude_set = set(args.exclude)\n    prompt_template = '{comment}{exclude}{command}{prefix}{name}'\n    for spec, mod in modules:\n        d['exclude'] = '## ' if spec.name in exclude_set else ''\n        d['comment'] = '' if not args.shell else '# {0}\\n'.format(\n            spec.format())\n        d['name'] = mod\n        out.write(prompt_template.format(**d))\n        out.write('\\n')\n\n\ndef find(module_type, specs, args):\n    \"\"\"Retrieve paths or use names of module files\"\"\"\n\n    single_spec = one_spec_or_raise(specs)\n\n    if args.recurse_dependencies:\n        specs_to_retrieve = list(\n            single_spec.traverse(order='post', cover='nodes',\n                                 deptype=('link', 'run')))\n    else:\n        specs_to_retrieve = [single_spec]\n\n    try:\n        modules = [spack.modules.common.get_module(module_type, spec,\n                                                   args.full_path)\n                   for spec in specs_to_retrieve]\n    except spack.modules.common.ModuleNotFoundError as e:\n        tty.die(e.message)\n    print(' '.join(modules))\n\n\ndef rm(module_type, specs, args):\n    \"\"\"Deletes the module files associated with every spec in specs, for every\n    module type in module types.\n    \"\"\"\n\n    module_cls = spack.modules.module_types[module_type]\n    module_exist = lambda x: os.path.exists(module_cls(x).layout.filename)\n\n    specs_with_modules = [spec for spec in specs if module_exist(spec)]\n\n    modules = [module_cls(spec) for spec in specs_with_modules]\n\n    if not modules:\n        tty.die('No module file matches your query')\n\n    # Ask for confirmation\n    if not args.yes_to_all:\n        msg = 'You are about to remove {0} module files for:\\n'\n        tty.msg(msg.format(module_type))\n        spack.cmd.display_specs(specs_with_modules, long=True)\n        print('')\n        answer = tty.get_yes_or_no('Do you want to proceed?')\n        if not answer:\n            tty.die('Will not remove any module files')\n\n    # Remove the module files\n    for s in modules:\n        s.remove()\n\n\ndef refresh(module_type, specs, args):\n    \"\"\"Regenerates the module files for every spec in specs and every module\n    type in module types.\n    \"\"\"\n\n    # Prompt a message to the user about what is going to change\n    if not specs:\n        tty.msg('No package matches your query')\n        return\n\n    if not args.upstream_modules:\n        specs = list(s for s in specs if not s.package.installed_upstream)\n\n    if not args.yes_to_all:\n        msg = 'You are about to regenerate {types} module files for:\\n'\n        tty.msg(msg.format(types=module_type))\n        spack.cmd.display_specs(specs, long=True)\n        print('')\n        answer = tty.get_yes_or_no('Do you want to proceed?')\n        if not answer:\n            tty.die('Module file regeneration aborted.')\n\n    # Cycle over the module types and regenerate module files\n\n    cls = spack.modules.module_types[module_type]\n\n    # Skip unknown packages.\n    writers = [\n        cls(spec) for spec in specs\n        if spack.repo.path.exists(spec.name)]\n\n    # Filter blacklisted packages early\n    writers = [x for x in writers if not x.conf.blacklisted]\n\n    # Detect name clashes in module files\n    file2writer = collections.defaultdict(list)\n    for item in writers:\n        file2writer[item.layout.filename].append(item)\n\n    if len(file2writer) != len(writers):\n        message = 'Name clashes detected in module files:\\n'\n        for filename, writer_list in file2writer.items():\n            if len(writer_list) > 1:\n                message += '\\nfile: {0}\\n'.format(filename)\n                for x in writer_list:\n                    message += 'spec: {0}\\n'.format(x.spec.format())\n        tty.error(message)\n        tty.error('Operation aborted')\n        raise SystemExit(1)\n\n    if len(writers) == 0:\n        msg = 'Nothing to be done for {0} module files.'\n        tty.msg(msg.format(module_type))\n        return\n\n    # If we arrived here we have at least one writer\n    module_type_root = writers[0].layout.dirname()\n    spack.modules.common.generate_module_index(module_type_root, writers)\n    # Proceed regenerating module files\n    tty.msg('Regenerating {name} module files'.format(name=module_type))\n    if os.path.isdir(module_type_root) and args.delete_tree:\n        shutil.rmtree(module_type_root, ignore_errors=False)\n    filesystem.mkdirp(module_type_root)\n    for x in writers:\n        try:\n            x.write(overwrite=True)\n        except Exception as e:\n            tty.debug(e)\n            msg = 'Could not write module file [{0}]'\n            tty.warn(msg.format(x.layout.filename))\n            tty.warn('\\t--> {0} <--'.format(str(e)))\n\n\n#: Dictionary populated with the list of sub-commands.\n#: Each sub-command must be callable and accept 3 arguments:\n#:\n#:   - module_type: the type of module it refers to\n#:   - specs : the list of specs to be processed\n#:   - args : namespace containing the parsed command line arguments\ncallbacks = {\n    'refresh': refresh,\n    'rm': rm,\n    'find': find,\n    'loads': loads\n}\n\n\ndef modules_cmd(parser, args, module_type, callbacks=callbacks):\n\n    # Qualifiers to be used when querying the db for specs\n    constraint_qualifiers = {\n        'refresh': {\n            'installed': True,\n            'known': True\n        },\n    }\n    query_args = constraint_qualifiers.get(args.subparser_name, {})\n\n    # Get the specs that match the query from the DB\n    specs = args.specs(**query_args)\n\n    try:\n\n        callbacks[args.subparser_name](module_type, specs, args)\n\n    except MultipleSpecsMatch:\n        msg = \"the constraint '{query}' matches multiple packages:\\n\"\n        for s in specs:\n            spec_fmt = '{hash:7} {name}{@version}{%compiler}'\n            spec_fmt += '{compiler_flags}{variants}{arch=architecture}'\n            msg += '\\t' + s.cformat(spec_fmt) + '\\n'\n        tty.error(msg.format(query=args.constraint))\n        tty.die('In this context exactly **one** match is needed: please specify your constraints better.')  # NOQA: ignore=E501\n\n    except NoSpecMatches:\n        msg = \"the constraint '{query}' matches no package.\"\n        tty.error(msg.format(query=args.constraint))\n        tty.die('In this context exactly **one** match is needed: please specify your constraints better.')  # NOQA: ignore=E501\n", "path": "lib/spack/spack/cmd/modules/__init__.py"}], "after_files": [{"content": "# Copyright 2013-2019 Lawrence Livermore National Security, LLC and other\n# Spack Project Developers. See the top-level COPYRIGHT file for details.\n#\n# SPDX-License-Identifier: (Apache-2.0 OR MIT)\n\n\"\"\"Implementation details of the ``spack module`` command.\"\"\"\n\nimport collections\nimport os.path\nimport shutil\nimport sys\n\nfrom llnl.util import filesystem, tty\n\nimport spack.cmd\nimport spack.modules\nimport spack.repo\nimport spack.modules.common\n\nimport spack.cmd.common.arguments as arguments\n\ndescription = \"manipulate module files\"\nsection = \"environment\"\nlevel = \"short\"\n\n\ndef setup_parser(subparser):\n    sp = subparser.add_subparsers(metavar='SUBCOMMAND', dest='subparser_name')\n\n    refresh_parser = sp.add_parser('refresh', help='regenerate module files')\n    refresh_parser.add_argument(\n        '--delete-tree',\n        help='delete the module file tree before refresh',\n        action='store_true'\n    )\n    refresh_parser.add_argument(\n        '--upstream-modules',\n        help='generate modules for packages installed upstream',\n        action='store_true'\n    )\n    arguments.add_common_arguments(\n        refresh_parser, ['constraint', 'yes_to_all']\n    )\n\n    find_parser = sp.add_parser('find', help='find module files for packages')\n    find_parser.add_argument(\n        '--full-path',\n        help='display full path to module file',\n        action='store_true'\n    )\n    arguments.add_common_arguments(\n        find_parser, ['constraint', 'recurse_dependencies']\n    )\n\n    rm_parser = sp.add_parser('rm', help='remove module files')\n    arguments.add_common_arguments(\n        rm_parser, ['constraint', 'yes_to_all']\n    )\n\n    loads_parser = sp.add_parser(\n        'loads',\n        help='prompt the list of modules associated with a constraint'\n    )\n    add_loads_arguments(loads_parser)\n    arguments.add_common_arguments(loads_parser, ['constraint'])\n\n    return sp\n\n\ndef add_loads_arguments(subparser):\n    subparser.add_argument(\n        '--input-only', action='store_false', dest='shell',\n        help='generate input for module command (instead of a shell script)'\n    )\n    subparser.add_argument(\n        '-p', '--prefix', dest='prefix', default='',\n        help='prepend to module names when issuing module load commands'\n    )\n    subparser.add_argument(\n        '-x', '--exclude', dest='exclude', action='append', default=[],\n        help=\"exclude package from output; may be specified multiple times\"\n    )\n    arguments.add_common_arguments(\n        subparser, ['recurse_dependencies']\n    )\n\n\nclass MultipleSpecsMatch(Exception):\n    \"\"\"Raised when multiple specs match a constraint, in a context where\n    this is not allowed.\n    \"\"\"\n\n\nclass NoSpecMatches(Exception):\n    \"\"\"Raised when no spec matches a constraint, in a context where\n    this is not allowed.\n    \"\"\"\n\n\ndef one_spec_or_raise(specs):\n    \"\"\"Ensures exactly one spec has been selected, or raises the appropriate\n    exception.\n    \"\"\"\n    # Ensure a single spec matches the constraint\n    if len(specs) == 0:\n        raise NoSpecMatches()\n    if len(specs) > 1:\n        raise MultipleSpecsMatch()\n\n    # Get the spec and module type\n    return specs[0]\n\n\ndef loads(module_type, specs, args, out=sys.stdout):\n    \"\"\"Prompt the list of modules associated with a list of specs\"\"\"\n\n    # Get a comprehensive list of specs\n    if args.recurse_dependencies:\n        specs_from_user_constraint = specs[:]\n        specs = []\n        # FIXME : during module file creation nodes seem to be visited\n        # FIXME : multiple times even if cover='nodes' is given. This\n        # FIXME : work around permits to get a unique list of spec anyhow.\n        # FIXME : (same problem as in spack/modules.py)\n        seen = set()\n        seen_add = seen.add\n        for spec in specs_from_user_constraint:\n            specs.extend(\n                [item for item in spec.traverse(order='post', cover='nodes')\n                 if not (item in seen or seen_add(item))]\n            )\n\n    modules = list(\n        (spec, spack.modules.common.get_module(module_type, spec, False))\n        for spec in specs)\n\n    module_commands = {\n        'tcl': 'module load ',\n        'lmod': 'module load ',\n        'dotkit': 'use '\n    }\n\n    d = {\n        'command': '' if not args.shell else module_commands[module_type],\n        'prefix': args.prefix\n    }\n\n    exclude_set = set(args.exclude)\n    prompt_template = '{comment}{exclude}{command}{prefix}{name}'\n    for spec, mod in modules:\n        d['exclude'] = '## ' if spec.name in exclude_set else ''\n        d['comment'] = '' if not args.shell else '# {0}\\n'.format(\n            spec.format())\n        d['name'] = mod\n        out.write(prompt_template.format(**d))\n        out.write('\\n')\n\n\ndef find(module_type, specs, args):\n    \"\"\"Retrieve paths or use names of module files\"\"\"\n\n    single_spec = one_spec_or_raise(specs)\n\n    if args.recurse_dependencies:\n        specs_to_retrieve = list(\n            single_spec.traverse(order='post', cover='nodes',\n                                 deptype=('link', 'run')))\n    else:\n        specs_to_retrieve = [single_spec]\n\n    try:\n        modules = [spack.modules.common.get_module(module_type, spec,\n                                                   args.full_path)\n                   for spec in specs_to_retrieve]\n    except spack.modules.common.ModuleNotFoundError as e:\n        tty.die(e.message)\n    print(' '.join(modules))\n\n\ndef rm(module_type, specs, args):\n    \"\"\"Deletes the module files associated with every spec in specs, for every\n    module type in module types.\n    \"\"\"\n\n    module_cls = spack.modules.module_types[module_type]\n    module_exist = lambda x: os.path.exists(module_cls(x).layout.filename)\n\n    specs_with_modules = [spec for spec in specs if module_exist(spec)]\n\n    modules = [module_cls(spec) for spec in specs_with_modules]\n\n    if not modules:\n        tty.die('No module file matches your query')\n\n    # Ask for confirmation\n    if not args.yes_to_all:\n        msg = 'You are about to remove {0} module files for:\\n'\n        tty.msg(msg.format(module_type))\n        spack.cmd.display_specs(specs_with_modules, long=True)\n        print('')\n        answer = tty.get_yes_or_no('Do you want to proceed?')\n        if not answer:\n            tty.die('Will not remove any module files')\n\n    # Remove the module files\n    for s in modules:\n        s.remove()\n\n\ndef refresh(module_type, specs, args):\n    \"\"\"Regenerates the module files for every spec in specs and every module\n    type in module types.\n    \"\"\"\n\n    # Prompt a message to the user about what is going to change\n    if not specs:\n        tty.msg('No package matches your query')\n        return\n\n    if not args.upstream_modules:\n        specs = list(s for s in specs if not s.package.installed_upstream)\n\n    if not args.yes_to_all:\n        msg = 'You are about to regenerate {types} module files for:\\n'\n        tty.msg(msg.format(types=module_type))\n        spack.cmd.display_specs(specs, long=True)\n        print('')\n        answer = tty.get_yes_or_no('Do you want to proceed?')\n        if not answer:\n            tty.die('Module file regeneration aborted.')\n\n    # Cycle over the module types and regenerate module files\n\n    cls = spack.modules.module_types[module_type]\n\n    # Skip unknown packages.\n    writers = [\n        cls(spec) for spec in specs\n        if spack.repo.path.exists(spec.name)]\n\n    # Filter blacklisted packages early\n    writers = [x for x in writers if not x.conf.blacklisted]\n\n    # Detect name clashes in module files\n    file2writer = collections.defaultdict(list)\n    for item in writers:\n        file2writer[item.layout.filename].append(item)\n\n    if len(file2writer) != len(writers):\n        message = 'Name clashes detected in module files:\\n'\n        for filename, writer_list in file2writer.items():\n            if len(writer_list) > 1:\n                message += '\\nfile: {0}\\n'.format(filename)\n                for x in writer_list:\n                    message += 'spec: {0}\\n'.format(x.spec.format())\n        tty.error(message)\n        tty.error('Operation aborted')\n        raise SystemExit(1)\n\n    if len(writers) == 0:\n        msg = 'Nothing to be done for {0} module files.'\n        tty.msg(msg.format(module_type))\n        return\n\n    # If we arrived here we have at least one writer\n    module_type_root = writers[0].layout.dirname()\n    spack.modules.common.generate_module_index(module_type_root, writers)\n    # Proceed regenerating module files\n    tty.msg('Regenerating {name} module files'.format(name=module_type))\n    if os.path.isdir(module_type_root) and args.delete_tree:\n        shutil.rmtree(module_type_root, ignore_errors=False)\n    filesystem.mkdirp(module_type_root)\n    for x in writers:\n        try:\n            x.write(overwrite=True)\n        except Exception as e:\n            tty.debug(e)\n            msg = 'Could not write module file [{0}]'\n            tty.warn(msg.format(x.layout.filename))\n            tty.warn('\\t--> {0} <--'.format(str(e)))\n\n\n#: Dictionary populated with the list of sub-commands.\n#: Each sub-command must be callable and accept 3 arguments:\n#:\n#:   - module_type: the type of module it refers to\n#:   - specs : the list of specs to be processed\n#:   - args : namespace containing the parsed command line arguments\ncallbacks = {\n    'refresh': refresh,\n    'rm': rm,\n    'find': find,\n    'loads': loads\n}\n\n\ndef modules_cmd(parser, args, module_type, callbacks=callbacks):\n\n    # Qualifiers to be used when querying the db for specs\n    constraint_qualifiers = {\n        'refresh': {\n            'installed': True,\n            'known': True\n        },\n    }\n    query_args = constraint_qualifiers.get(args.subparser_name, {})\n\n    # Get the specs that match the query from the DB\n    specs = args.specs(**query_args)\n\n    try:\n\n        callbacks[args.subparser_name](module_type, specs, args)\n\n    except MultipleSpecsMatch:\n        msg = \"the constraint '{query}' matches multiple packages:\\n\"\n        for s in specs:\n            spec_fmt = '{hash:7} {name}{@version}{%compiler}'\n            spec_fmt += '{compiler_flags}{variants}{arch=architecture}'\n            msg += '\\t' + s.cformat(spec_fmt) + '\\n'\n        tty.error(msg.format(query=args.constraint))\n        tty.die('In this context exactly **one** match is needed: please specify your constraints better.')  # NOQA: ignore=E501\n\n    except NoSpecMatches:\n        msg = \"the constraint '{query}' matches no package.\"\n        tty.error(msg.format(query=args.constraint))\n        tty.die('In this context exactly **one** match is needed: please specify your constraints better.')  # NOQA: ignore=E501\n", "path": "lib/spack/spack/cmd/modules/__init__.py"}]}