-
Notifications
You must be signed in to change notification settings - Fork 117
[feat] Re-implement pipeline hook mechanism and add a post-init hook #1865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
5fdf0ca
Add post-init hook
7d17ba3
Allow running pre-init hooks, but only if set by the framework
e39da2f
Expand unit tests
b3c4691
Merge branch 'master' into feat/post-init-hook
f1047f9
Reimplement pipeline hooks
1fbeb38
Update docstring for post-init hooks
8ccc22e
Address PR comments (I)
6390cb1
Reimplement the pipeline hook mechanism
721d052
Merge branch 'master' into feat/post-init-hook
d06bf2d
Fix unit tests after merge
89a4ebc
Remove unused imports
2d19132
Overload __getattribute__ for the pipeline stages
jjotero c090bc7
Remove unused imports
jjotero c1d4f57
Merge pull request #1 from jjotero/feat/simplify-hook-access
b485d9d
Further implementation improvements
0c107b7
Merge branch 'master' into feat/post-init-hook
9ac7e9f
Properly restore runtime in `test_policies.py` unit tests
59e9f57
Better implementation of the `make_async_exec_ctx` fixture
85bbc9c
Address PR comments (II)
985d774
Merge branch 'master' into feat/post-init-hook
ebd2ea0
Raise `ValueError` for invalid pipeline stages
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| # Copyright 2016-2021 Swiss National Supercomputing Centre (CSCS/ETH Zurich) | ||
| # ReFrame Project Developers. See the top-level LICENSE file for details. | ||
| # | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
|
|
||
| import contextlib | ||
| import functools | ||
|
|
||
| import reframe.utility as util | ||
|
|
||
|
|
||
| def attach_hooks(hooks): | ||
| '''Attach pipeline hooks to phase ``name''. | ||
|
|
||
| This function returns a decorator for pipeline functions that will run the | ||
| registered hooks before and after the function. | ||
|
|
||
| If ``name'' is :class:`None`, both pre- and post-hooks will run, otherwise | ||
| only the hooks of the phase ``name'' will be executed. | ||
| ''' | ||
|
|
||
| def _deco(func): | ||
| def select_hooks(obj, kind): | ||
| phase = kind + func.__name__ | ||
| if phase not in hooks: | ||
| return [] | ||
|
|
||
| return [h for h in hooks[phase] | ||
| if h.__name__ not in obj._disabled_hooks] | ||
|
|
||
| @functools.wraps(func) | ||
| def _fn(obj, *args, **kwargs): | ||
| for h in select_hooks(obj, 'pre_'): | ||
| h(obj) | ||
|
|
||
| func(obj, *args, **kwargs) | ||
| for h in select_hooks(obj, 'post_'): | ||
| h(obj) | ||
|
|
||
| return _fn | ||
|
|
||
| return _deco | ||
|
|
||
|
|
||
| class Hook: | ||
| '''A pipeline hook. | ||
|
|
||
| This is essentially a function wrapper that hashes the functions by name, | ||
| since we want hooks to be overriden by name in subclasses. | ||
| ''' | ||
|
|
||
| def __init__(self, fn): | ||
| self.__fn = fn | ||
|
|
||
| def __getattr__(self, attr): | ||
| return getattr(self.__fn, attr) | ||
|
|
||
| @property | ||
| def fn(self): | ||
| return self.__fn | ||
|
|
||
| def __hash__(self): | ||
| return hash(self.__name__) | ||
|
|
||
| def __eq__(self, other): | ||
| if not isinstance(other, type(self)): | ||
| return NotImplemented | ||
|
|
||
| return self.__name__ == other.__name__ | ||
|
|
||
| def __call__(self, *args, **kwargs): | ||
| return self.__fn(*args, **kwargs) | ||
|
|
||
| def __repr__(self): | ||
| return repr(self.__fn) | ||
|
|
||
|
|
||
| class HookRegistry: | ||
| '''Global hook registry.''' | ||
|
|
||
| @classmethod | ||
| def create(cls, namespace): | ||
| '''Create a hook registry from a class namespace. | ||
|
|
||
| Hook functions have an `_rfm_attach` attribute that specify the stages | ||
| of the pipeline where they must be attached. Dependencies will be | ||
| resolved first in the post-setup phase if not assigned elsewhere. | ||
| ''' | ||
|
|
||
| local_hooks = {} | ||
| fn_with_deps = [] | ||
| for v in namespace.values(): | ||
| if hasattr(v, '_rfm_attach'): | ||
| for phase in v._rfm_attach: | ||
| try: | ||
| local_hooks[phase].append(Hook(v)) | ||
| except KeyError: | ||
| local_hooks[phase] = [Hook(v)] | ||
|
|
||
| with contextlib.suppress(AttributeError): | ||
| if v._rfm_resolve_deps: | ||
| fn_with_deps.append(Hook(v)) | ||
|
|
||
| if fn_with_deps: | ||
| local_hooks['post_setup'] = ( | ||
| fn_with_deps + local_hooks.get('post_setup', []) | ||
| ) | ||
|
|
||
| return cls(local_hooks) | ||
|
|
||
| def __init__(self, hooks=None): | ||
| self.__hooks = {} | ||
| if hooks is not None: | ||
vkarak marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self.update(hooks) | ||
|
|
||
| def __getitem__(self, key): | ||
| return self.__hooks[key] | ||
|
|
||
| def __setitem__(self, key, name): | ||
| self.__hooks[key] = name | ||
|
|
||
| def __contains__(self, key): | ||
| return key in self.__hooks | ||
|
|
||
| def __getattr__(self, name): | ||
| return getattr(self.__hooks, name) | ||
|
|
||
| def update(self, hooks): | ||
jjotero marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| for phase, hks in hooks.items(): | ||
| self.__hooks.setdefault(phase, util.OrderedSet()) | ||
| for h in hks: | ||
| self.__hooks[phase].add(h) | ||
|
|
||
| def __repr__(self): | ||
| return repr(self.__hooks) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.