-
Notifications
You must be signed in to change notification settings - Fork 26
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
Fix pre- and post-hooks #133
Merged
nden
merged 20 commits into
spacetelescope:main
from
jdavies-st:add-step-as-hook-validation
Mar 8, 2024
Merged
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
2cdce26
Allow pre- and post-hooks to be Step subclass or instance of the subc…
jdavies-st a539df8
Finish pre- and post-hook updates; add tests
jdavies-st 5d9b550
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 6b03ac2
Do importorskip correctly
jdavies-st 8e4fb38
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] 42451e1
Update CHANGES.rst
jdavies-st 53834f3
Add coverage to jwst CI workflow job
jdavies-st 124fbbf
Run coverage with jwst and stdatamodels dependencies
jdavies-st d517cd3
Use dict literals instead of dict()
jdavies-st 48c388e
Fix downstreamdeps in tox
jdavies-st 230d00c
Fix roman_datamodels import
jdavies-st bd522c1
Fix ruff issues
jdavies-st c260e01
Fix importorskip in test_hooks; add tmp_cwd fixture
jdavies-st 7ef6c04
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] b338b68
Add CRDS server URL to downstreamdeps tox env
jdavies-st 54aed99
Make ruff happy
jdavies-st 463cc96
Add test for import_func to test error path
jdavies-st 6187ff9
Allow hook to be string of Step instance
jdavies-st 93305c4
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] c2810ea
Impelement review comments
jdavies-st 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 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 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 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 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 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 |
---|---|---|
@@ -1,40 +1,108 @@ | ||
""" | ||
Pre- and post-hooks | ||
""" | ||
import contextlib | ||
import types | ||
import ast | ||
import inspect | ||
|
||
from . import utilities | ||
from . import function_wrapper, utilities | ||
from .step import Step | ||
|
||
|
||
def hook_from_string(step, type, num, command): # noqa: A002 | ||
name = f"{type}_hook{num:d}" | ||
def hook_from_string(step, hooktype, num, command): | ||
""" | ||
Generate hook from string, function, Step or Step instance | ||
|
||
step_class = None | ||
with contextlib.suppress(Exception): | ||
step_class = utilities.import_class(command, Step, step.config_file) | ||
Parameters | ||
---------- | ||
step : `stpipe.step.Step` | ||
Parent step instance to which this hook is attached, i.e. "self" | ||
hooktype : str, "pre" or "post" | ||
Type of hook pre-hook , or post-hook | ||
num : int | ||
The number, in order, of the pre- or post-hook in the list | ||
command : str or `stpipe.step.Step` instance | ||
|
||
if step_class is not None: | ||
return step_class(name, parent=step, config_file=step.config_file) | ||
Returns | ||
------- | ||
`stpipe.step.Step` | ||
""" | ||
name = f"{hooktype}_hook{num:d}" | ||
|
||
step_class = None | ||
step_func = None | ||
with contextlib.suppress(Exception): | ||
step_func = utilities.import_class( | ||
command, types.FunctionType, step.config_file | ||
) | ||
|
||
if step_func is not None: | ||
from . import function_wrapper | ||
# hook is a string of the fully-qualified name of a class or function | ||
if isinstance(command, str): | ||
try: | ||
# String points to a Step subclass | ||
step_class = utilities.import_class( | ||
command, subclassof=Step, config_file=step.config_file | ||
) | ||
except ImportError: | ||
# String is possibly a subproc, so handle this later | ||
pass | ||
except AttributeError: | ||
# String points to an instance of a Step | ||
# So import the class | ||
class_string, _, params = command.partition("(") | ||
step_class = utilities.import_class( | ||
class_string, subclassof=Step, config_file=step.config_file | ||
) | ||
# Then convert rest of string to args and instantiate the class | ||
kwargs_string = params.strip(")") | ||
expr = ast.parse(f"dict({kwargs_string}\n)", mode="eval") | ||
kwargs = {kw.arg: ast.literal_eval(kw.value) for kw in expr.body.keywords} | ||
return step_class(**kwargs) | ||
except TypeError: | ||
# String points to a function | ||
step_func = utilities.import_func(command) | ||
else: | ||
if step_class.class_alias is not None: | ||
name = step_class.class_alias | ||
return step_class(name, parent=step, config_file=step.config_file) | ||
|
||
# hook is a string of the fully-qualified name of a function | ||
if step_func is not None: | ||
return function_wrapper.FunctionWrapper( | ||
step_func, parent=step, config_file=step.config_file | ||
) | ||
|
||
return function_wrapper.FunctionWrapper( | ||
step_func, parent=step, config_file=step.config_file | ||
) | ||
# hook is an already-imported Step subclass | ||
if inspect.isclass(command) and issubclass(command, Step): | ||
step_class = command | ||
if step_class.class_alias is not None: | ||
name = step_class.class_alias | ||
return step_class(name, parent=step, config_file=step.config_file) | ||
|
||
# hook is an instance of a Step subclass | ||
if isinstance(command, Step): | ||
if command.class_alias is not None: | ||
command.name = command.class_alias | ||
else: | ||
command.name = name | ||
return command | ||
|
||
# hook is a command-line script or system call | ||
from .subproc import SystemCall | ||
|
||
return SystemCall(name, parent=step, command=command) | ||
|
||
|
||
def get_hook_objects(step, type_, hooks): | ||
return [hook_from_string(step, type_, i, hook) for i, hook in enumerate(hooks)] | ||
def get_hook_objects(step, hooktype, hooks): | ||
""" | ||
Get list of pre- or post-hooks for a step | ||
|
||
Parameters | ||
---------- | ||
step : `stpipe.step.Step` | ||
instance to which this is a hook | ||
hooktype : str, "pre" or "post" | ||
strings, to indicate whether it is a pre- or post-hook | ||
hooks : str or class | ||
path to executable script, or Step class to run as hook | ||
|
||
Returns | ||
------- | ||
list of callables that can be run as a hook | ||
""" | ||
return [hook_from_string(step, hooktype, i, hook) for i, hook in enumerate(hooks)] |
This file contains 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 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 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 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,15 @@ | ||
import os | ||
from pathlib import Path | ||
|
||
import pytest | ||
|
||
|
||
@pytest.fixture() | ||
def tmp_cwd(tmp_path): | ||
"""Perform test in a pristine temporary working directory.""" | ||
old_dir = Path.cwd() | ||
os.chdir(tmp_path) | ||
try: | ||
yield tmp_path | ||
finally: | ||
os.chdir(old_dir) |
This file contains 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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be deprecated instead of removed? It looks like it's been available for a long time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not removed, I've renamed the
hook_from_string()
function tohook_from_string_or_class()
to better describe what it actually does. Happy to change the name back if you think this will affect the public API. Since this is all internal workings, I would rather make all this private, including the wholehooks.py
module, but happy to maintain backwards compat.If not maintaining backwards compat, maybe "hook_from_object()` would be a better function name.
stpipe
suffers from a lot of its internal workings, such asStep.run()
, being public functions.As far as I can tell, a search of Github doesn't turn up any uses of
stpipe.hooks
outside ofstpipe
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've gone ahead and changed the function name back to its original name, and just clarified in the docstring what it does. We can revisit moving public functions and methods to be private in a future refactor.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks! And I agree it's unlikely anyone uses it.