diff options
author | Massimiliano Culpo <massimiliano.culpo@gmail.com> | 2021-04-28 01:55:07 +0200 |
---|---|---|
committer | Todd Gamblin <tgamblin@llnl.gov> | 2021-05-22 11:51:21 -0700 |
commit | 13fed376f20f11221b83a806cc66e4ae82918c4d (patch) | |
tree | 4b8f898bd978ccf9dc0c66dfdacfb5c54c4436d6 | |
parent | fb27c7ad0c5542c10de98de6c825fbd0c7c8a88f (diff) | |
download | spack-13fed376f20f11221b83a806cc66e4ae82918c4d.tar.gz spack-13fed376f20f11221b83a806cc66e4ae82918c4d.tar.bz2 spack-13fed376f20f11221b83a806cc66e4ae82918c4d.tar.xz spack-13fed376f20f11221b83a806cc66e4ae82918c4d.zip |
Import hooks using Python's built-in machinery (#23288)
The function we coded in Spack to load Python modules with arbitrary
names from a file seem to have issues with local imports. For
loading hooks though it is unnecessary to use such functions, since
we don't care to bind a custom name to a module nor we have to load
it from an unknown location.
This PR thus modifies spack.hook in the following ways:
- Use __import__ instead of spack.util.imp.load_source (this
addresses #20005)
- Sync module docstring with all the hooks we have
- Avoid using memoization in a module function
- Marked with a leading underscore all the names that are supposed
to stay local
-rw-r--r-- | lib/spack/spack/hooks/__init__.py | 67 |
1 files changed, 38 insertions, 29 deletions
diff --git a/lib/spack/spack/hooks/__init__.py b/lib/spack/spack/hooks/__init__.py index 5a39f242fd..5a5a04b49f 100644 --- a/lib/spack/spack/hooks/__init__.py +++ b/lib/spack/spack/hooks/__init__.py @@ -2,8 +2,8 @@ # Spack Project Developers. See the top-level COPYRIGHT file for details. # # SPDX-License-Identifier: (Apache-2.0 OR MIT) - """This package contains modules with hooks for various stages in the + Spack install process. You can add modules here and they'll be executed by package at various times during the package lifecycle. @@ -21,46 +21,55 @@ systems (e.g. modules, lmod, etc.) or to add other custom features. """ -import os.path - +import llnl.util.lang import spack.paths -import spack.util.imp as simp -from llnl.util.lang import memoized, list_modules - -@memoized -def all_hook_modules(): - modules = [] - for name in list_modules(spack.paths.hooks_path): - mod_name = __name__ + '.' + name - path = os.path.join(spack.paths.hooks_path, name) + ".py" - mod = simp.load_source(mod_name, path) - if name == 'write_install_manifest': - last_mod = mod - else: - modules.append(mod) - - # put `write_install_manifest` as the last hook to run - modules.append(last_mod) - return modules - - -class HookRunner(object): +class _HookRunner(object): + #: Stores all hooks on first call, shared among + #: all HookRunner objects + _hooks = None def __init__(self, hook_name): self.hook_name = hook_name + @classmethod + def _populate_hooks(cls): + # Lazily populate the list of hooks + cls._hooks = [] + relative_names = list(llnl.util.lang.list_modules( + spack.paths.hooks_path + )) + + # We want this hook to be the last registered + relative_names.sort(key=lambda x: x == 'write_install_manifest') + assert relative_names[-1] == 'write_install_manifest' + + for name in relative_names: + module_name = __name__ + '.' + name + # When importing a module from a package, __import__('A.B', ...) + # returns package A when 'fromlist' is empty. If fromlist is not + # empty it returns the submodule B instead + # See: https://stackoverflow.com/a/2725668/771663 + module_obj = __import__(module_name, fromlist=[None]) + cls._hooks.append((module_name, module_obj)) + + @property + def hooks(self): + if not self._hooks: + self._populate_hooks() + return self._hooks + def __call__(self, *args, **kwargs): - for module in all_hook_modules(): + for _, module in self.hooks: if hasattr(module, self.hook_name): hook = getattr(module, self.hook_name) if hasattr(hook, '__call__'): hook(*args, **kwargs) -pre_install = HookRunner('pre_install') -post_install = HookRunner('post_install') +pre_install = _HookRunner('pre_install') +post_install = _HookRunner('post_install') -pre_uninstall = HookRunner('pre_uninstall') -post_uninstall = HookRunner('post_uninstall') +pre_uninstall = _HookRunner('pre_uninstall') +post_uninstall = _HookRunner('post_uninstall') |