hpcflow-new2 0.2.0a189__py3-none-any.whl → 0.2.0a199__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hpcflow/__pyinstaller/hook-hpcflow.py +9 -6
- hpcflow/_version.py +1 -1
- hpcflow/app.py +1 -0
- hpcflow/data/scripts/bad_script.py +2 -0
- hpcflow/data/scripts/do_nothing.py +2 -0
- hpcflow/data/scripts/env_specifier_test/input_file_generator_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/main_script_test_pass_env_spec.py +8 -0
- hpcflow/data/scripts/env_specifier_test/output_file_parser_pass_env_spec.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/input_file_generator_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v1/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/env_specifier_test/v1/output_file_parser_basic.py +4 -0
- hpcflow/data/scripts/env_specifier_test/v2/main_script_test_direct_in_direct_out.py +7 -0
- hpcflow/data/scripts/input_file_generator_basic.py +3 -0
- hpcflow/data/scripts/input_file_generator_basic_FAIL.py +3 -0
- hpcflow/data/scripts/input_file_generator_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/main_script_test_direct_in.py +3 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_2_fail_allowed_group.py +7 -0
- hpcflow/data/scripts/main_script_test_direct_in_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_direct_in_group_one_fail_direct_out_3.py +6 -0
- hpcflow/data/scripts/main_script_test_hdf5_in_obj.py +1 -1
- hpcflow/data/scripts/main_script_test_hdf5_in_obj_2.py +12 -0
- hpcflow/data/scripts/main_script_test_hdf5_out_obj.py +1 -1
- hpcflow/data/scripts/main_script_test_json_out_FAIL.py +3 -0
- hpcflow/data/scripts/main_script_test_shell_env_vars.py +12 -0
- hpcflow/data/scripts/main_script_test_std_out_std_err.py +6 -0
- hpcflow/data/scripts/output_file_parser_basic.py +3 -0
- hpcflow/data/scripts/output_file_parser_basic_FAIL.py +7 -0
- hpcflow/data/scripts/output_file_parser_test_stdout_stderr.py +8 -0
- hpcflow/data/scripts/script_exit_test.py +5 -0
- hpcflow/data/template_components/environments.yaml +1 -1
- hpcflow/sdk/__init__.py +26 -15
- hpcflow/sdk/app.py +2192 -768
- hpcflow/sdk/cli.py +506 -296
- hpcflow/sdk/cli_common.py +105 -7
- hpcflow/sdk/config/__init__.py +1 -1
- hpcflow/sdk/config/callbacks.py +115 -43
- hpcflow/sdk/config/cli.py +126 -103
- hpcflow/sdk/config/config.py +674 -318
- hpcflow/sdk/config/config_file.py +131 -95
- hpcflow/sdk/config/errors.py +125 -84
- hpcflow/sdk/config/types.py +148 -0
- hpcflow/sdk/core/__init__.py +25 -1
- hpcflow/sdk/core/actions.py +1771 -1059
- hpcflow/sdk/core/app_aware.py +24 -0
- hpcflow/sdk/core/cache.py +139 -79
- hpcflow/sdk/core/command_files.py +263 -287
- hpcflow/sdk/core/commands.py +145 -112
- hpcflow/sdk/core/element.py +828 -535
- hpcflow/sdk/core/enums.py +192 -0
- hpcflow/sdk/core/environment.py +74 -93
- hpcflow/sdk/core/errors.py +455 -52
- hpcflow/sdk/core/execute.py +207 -0
- hpcflow/sdk/core/json_like.py +540 -272
- hpcflow/sdk/core/loop.py +751 -347
- hpcflow/sdk/core/loop_cache.py +164 -47
- hpcflow/sdk/core/object_list.py +370 -207
- hpcflow/sdk/core/parameters.py +1100 -627
- hpcflow/sdk/core/rule.py +59 -41
- hpcflow/sdk/core/run_dir_files.py +21 -37
- hpcflow/sdk/core/skip_reason.py +7 -0
- hpcflow/sdk/core/task.py +1649 -1339
- hpcflow/sdk/core/task_schema.py +308 -196
- hpcflow/sdk/core/test_utils.py +191 -114
- hpcflow/sdk/core/types.py +440 -0
- hpcflow/sdk/core/utils.py +485 -309
- hpcflow/sdk/core/validation.py +82 -9
- hpcflow/sdk/core/workflow.py +2544 -1178
- hpcflow/sdk/core/zarr_io.py +98 -137
- hpcflow/sdk/data/workflow_spec_schema.yaml +2 -0
- hpcflow/sdk/demo/cli.py +53 -33
- hpcflow/sdk/helper/cli.py +18 -15
- hpcflow/sdk/helper/helper.py +75 -63
- hpcflow/sdk/helper/watcher.py +61 -28
- hpcflow/sdk/log.py +122 -71
- hpcflow/sdk/persistence/__init__.py +8 -31
- hpcflow/sdk/persistence/base.py +1360 -606
- hpcflow/sdk/persistence/defaults.py +6 -0
- hpcflow/sdk/persistence/discovery.py +38 -0
- hpcflow/sdk/persistence/json.py +568 -188
- hpcflow/sdk/persistence/pending.py +382 -179
- hpcflow/sdk/persistence/store_resource.py +39 -23
- hpcflow/sdk/persistence/types.py +318 -0
- hpcflow/sdk/persistence/utils.py +14 -11
- hpcflow/sdk/persistence/zarr.py +1337 -433
- hpcflow/sdk/runtime.py +44 -41
- hpcflow/sdk/submission/{jobscript_info.py → enums.py} +39 -12
- hpcflow/sdk/submission/jobscript.py +1651 -692
- hpcflow/sdk/submission/schedulers/__init__.py +167 -39
- hpcflow/sdk/submission/schedulers/direct.py +121 -81
- hpcflow/sdk/submission/schedulers/sge.py +170 -129
- hpcflow/sdk/submission/schedulers/slurm.py +291 -268
- hpcflow/sdk/submission/schedulers/utils.py +12 -2
- hpcflow/sdk/submission/shells/__init__.py +14 -15
- hpcflow/sdk/submission/shells/base.py +150 -29
- hpcflow/sdk/submission/shells/bash.py +283 -173
- hpcflow/sdk/submission/shells/os_version.py +31 -30
- hpcflow/sdk/submission/shells/powershell.py +228 -170
- hpcflow/sdk/submission/submission.py +1014 -335
- hpcflow/sdk/submission/types.py +140 -0
- hpcflow/sdk/typing.py +182 -12
- hpcflow/sdk/utils/arrays.py +71 -0
- hpcflow/sdk/utils/deferred_file.py +55 -0
- hpcflow/sdk/utils/hashing.py +16 -0
- hpcflow/sdk/utils/patches.py +12 -0
- hpcflow/sdk/utils/strings.py +33 -0
- hpcflow/tests/api/test_api.py +32 -0
- hpcflow/tests/conftest.py +27 -6
- hpcflow/tests/data/multi_path_sequences.yaml +29 -0
- hpcflow/tests/data/workflow_test_run_abort.yaml +34 -35
- hpcflow/tests/schedulers/sge/test_sge_submission.py +36 -0
- hpcflow/tests/schedulers/slurm/test_slurm_submission.py +5 -2
- hpcflow/tests/scripts/test_input_file_generators.py +282 -0
- hpcflow/tests/scripts/test_main_scripts.py +866 -85
- hpcflow/tests/scripts/test_non_snippet_script.py +46 -0
- hpcflow/tests/scripts/test_ouput_file_parsers.py +353 -0
- hpcflow/tests/shells/wsl/test_wsl_submission.py +12 -4
- hpcflow/tests/unit/test_action.py +262 -75
- hpcflow/tests/unit/test_action_rule.py +9 -4
- hpcflow/tests/unit/test_app.py +33 -6
- hpcflow/tests/unit/test_cache.py +46 -0
- hpcflow/tests/unit/test_cli.py +134 -1
- hpcflow/tests/unit/test_command.py +71 -54
- hpcflow/tests/unit/test_config.py +142 -16
- hpcflow/tests/unit/test_config_file.py +21 -18
- hpcflow/tests/unit/test_element.py +58 -62
- hpcflow/tests/unit/test_element_iteration.py +50 -1
- hpcflow/tests/unit/test_element_set.py +29 -19
- hpcflow/tests/unit/test_group.py +4 -2
- hpcflow/tests/unit/test_input_source.py +116 -93
- hpcflow/tests/unit/test_input_value.py +29 -24
- hpcflow/tests/unit/test_jobscript_unit.py +757 -0
- hpcflow/tests/unit/test_json_like.py +44 -35
- hpcflow/tests/unit/test_loop.py +1396 -84
- hpcflow/tests/unit/test_meta_task.py +325 -0
- hpcflow/tests/unit/test_multi_path_sequences.py +229 -0
- hpcflow/tests/unit/test_object_list.py +17 -12
- hpcflow/tests/unit/test_parameter.py +29 -7
- hpcflow/tests/unit/test_persistence.py +237 -42
- hpcflow/tests/unit/test_resources.py +20 -18
- hpcflow/tests/unit/test_run.py +117 -6
- hpcflow/tests/unit/test_run_directories.py +29 -0
- hpcflow/tests/unit/test_runtime.py +2 -1
- hpcflow/tests/unit/test_schema_input.py +23 -15
- hpcflow/tests/unit/test_shell.py +23 -2
- hpcflow/tests/unit/test_slurm.py +8 -7
- hpcflow/tests/unit/test_submission.py +38 -89
- hpcflow/tests/unit/test_task.py +352 -247
- hpcflow/tests/unit/test_task_schema.py +33 -20
- hpcflow/tests/unit/test_utils.py +9 -11
- hpcflow/tests/unit/test_value_sequence.py +15 -12
- hpcflow/tests/unit/test_workflow.py +114 -83
- hpcflow/tests/unit/test_workflow_template.py +0 -1
- hpcflow/tests/unit/utils/test_arrays.py +40 -0
- hpcflow/tests/unit/utils/test_deferred_file_writer.py +34 -0
- hpcflow/tests/unit/utils/test_hashing.py +65 -0
- hpcflow/tests/unit/utils/test_patches.py +5 -0
- hpcflow/tests/unit/utils/test_redirect_std.py +50 -0
- hpcflow/tests/workflows/__init__.py +0 -0
- hpcflow/tests/workflows/test_directory_structure.py +31 -0
- hpcflow/tests/workflows/test_jobscript.py +334 -1
- hpcflow/tests/workflows/test_run_status.py +198 -0
- hpcflow/tests/workflows/test_skip_downstream.py +696 -0
- hpcflow/tests/workflows/test_submission.py +140 -0
- hpcflow/tests/workflows/test_workflows.py +160 -15
- hpcflow/tests/workflows/test_zip.py +18 -0
- hpcflow/viz_demo.ipynb +6587 -3
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/METADATA +8 -4
- hpcflow_new2-0.2.0a199.dist-info/RECORD +221 -0
- hpcflow/sdk/core/parallel.py +0 -21
- hpcflow_new2-0.2.0a189.dist-info/RECORD +0 -158
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/LICENSE +0 -0
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/WHEEL +0 -0
- {hpcflow_new2-0.2.0a189.dist-info → hpcflow_new2-0.2.0a199.dist-info}/entry_points.txt +0 -0
@@ -1,16 +1,21 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from typing import cast, TYPE_CHECKING
|
1
3
|
from hpcflow.app import app as hf
|
2
4
|
|
5
|
+
if TYPE_CHECKING:
|
6
|
+
from hpcflow.sdk.core.types import RuleArgs
|
3
7
|
|
4
|
-
|
5
|
-
|
8
|
+
|
9
|
+
def test_equivalent_init_with_rule_args() -> None:
|
10
|
+
rule_args: RuleArgs = {
|
6
11
|
"path": "resources.os_name",
|
7
12
|
"condition": {"value.equal_to": "posix"},
|
8
13
|
}
|
9
14
|
assert hf.ActionRule(rule=hf.Rule(**rule_args)) == hf.ActionRule(**rule_args)
|
10
15
|
|
11
16
|
|
12
|
-
def test_equivalent_init_json_like_with_rule_args():
|
13
|
-
rule_args = {
|
17
|
+
def test_equivalent_init_json_like_with_rule_args() -> None:
|
18
|
+
rule_args: dict = {
|
14
19
|
"path": "resources.os_name",
|
15
20
|
"condition": {"value.equal_to": "posix"},
|
16
21
|
}
|
hpcflow/tests/unit/test_app.py
CHANGED
@@ -1,37 +1,45 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
from pathlib import Path
|
1
3
|
import sys
|
4
|
+
from typing import TYPE_CHECKING
|
2
5
|
import pytest
|
3
6
|
import requests
|
4
7
|
|
5
8
|
from hpcflow.app import app as hf
|
6
9
|
|
10
|
+
if TYPE_CHECKING:
|
11
|
+
from hpcflow.sdk.core.actions import Action, ActionEnvironment
|
12
|
+
|
7
13
|
|
8
14
|
@pytest.fixture
|
9
|
-
def act_env_1():
|
15
|
+
def act_env_1() -> ActionEnvironment:
|
10
16
|
return hf.ActionEnvironment(environment="env_1")
|
11
17
|
|
12
18
|
|
13
19
|
@pytest.fixture
|
14
|
-
def act_1(act_env_1):
|
20
|
+
def act_1(act_env_1) -> Action:
|
15
21
|
return hf.Action(
|
16
22
|
commands=[hf.Command("<<parameter:p1>>")],
|
17
23
|
environments=[act_env_1],
|
18
24
|
)
|
19
25
|
|
20
26
|
|
21
|
-
def test_shared_data_from_json_like_with_shared_data_dependency(act_1):
|
27
|
+
def test_shared_data_from_json_like_with_shared_data_dependency(act_1: Action):
|
22
28
|
"""Check we can generate some shared data objects where one depends on another."""
|
23
29
|
|
24
30
|
p1 = hf.Parameter("p1")
|
25
31
|
p1._set_hash()
|
26
32
|
p1_hash = p1._hash_value
|
33
|
+
assert p1_hash is not None
|
27
34
|
|
28
35
|
ts1 = hf.TaskSchema(objective="ts1", actions=[act_1], inputs=[p1])
|
29
36
|
ts1._set_hash()
|
30
37
|
ts1_hash = ts1._hash_value
|
38
|
+
assert ts1_hash is not None
|
31
39
|
|
32
40
|
env_label = ts1.actions[0].environments[0].environment
|
33
41
|
|
34
|
-
shared_data_json = {
|
42
|
+
shared_data_json: dict[str, dict] = {
|
35
43
|
"parameters": {
|
36
44
|
p1_hash: {
|
37
45
|
"is_file": p1.is_file,
|
@@ -85,7 +93,7 @@ def test_shared_data_from_json_like_with_shared_data_dependency(act_1):
|
|
85
93
|
] == hf.TaskSchemasList([ts1])
|
86
94
|
|
87
95
|
|
88
|
-
def test_get_demo_data_manifest(null_config):
|
96
|
+
def test_get_demo_data_manifest(null_config) -> None:
|
89
97
|
hf.get_demo_data_files_manifest()
|
90
98
|
|
91
99
|
|
@@ -97,9 +105,28 @@ def test_get_demo_data_manifest(null_config):
|
|
97
105
|
"retrieving demo data from GitHub."
|
98
106
|
),
|
99
107
|
)
|
100
|
-
def test_get_demo_data_cache(null_config):
|
108
|
+
def test_get_demo_data_cache(null_config) -> None:
|
101
109
|
hf.clear_demo_data_cache_dir()
|
102
110
|
hf.cache_demo_data_file("text_file.txt")
|
103
111
|
with hf.demo_data_cache_dir.joinpath("text_file.txt").open("rt") as fh:
|
104
112
|
contents = fh.read()
|
105
113
|
assert contents == "\n".join(f"{i}" for i in range(1, 11)) + "\n"
|
114
|
+
|
115
|
+
|
116
|
+
def test_list_demo_workflows():
|
117
|
+
# sanity checks
|
118
|
+
lst = hf.list_demo_workflows()
|
119
|
+
assert isinstance(lst, tuple)
|
120
|
+
assert all(isinstance(i, str) and "." not in i for i in lst) # no extension included
|
121
|
+
|
122
|
+
|
123
|
+
def test_get_demo_workflows():
|
124
|
+
# sanity checks
|
125
|
+
lst = hf.list_demo_workflows()
|
126
|
+
demo_paths = hf._get_demo_workflows()
|
127
|
+
# keys should be those in the list:
|
128
|
+
assert sorted(list(lst)) == sorted(list(demo_paths.keys()))
|
129
|
+
|
130
|
+
# values should be distinct, absolute paths:
|
131
|
+
assert all(isinstance(i, Path) and i.is_absolute() for i in demo_paths.values())
|
132
|
+
assert len(set(demo_paths.values())) == len(demo_paths)
|
@@ -0,0 +1,46 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
from hpcflow.sdk.core.cache import ObjectCache
|
3
|
+
from hpcflow.sdk.core.test_utils import make_workflow
|
4
|
+
|
5
|
+
|
6
|
+
def test_object_cache_dependencies_simple(tmp_path: Path):
|
7
|
+
wk = make_workflow(
|
8
|
+
schemas_spec=[
|
9
|
+
({"p1": None}, ("p2",), "t1"),
|
10
|
+
({"p2": None}, ("p3",), "t2"),
|
11
|
+
({"p3": None}, ("p4",), "t3"),
|
12
|
+
({"p4": None}, ("p5",), "t4"),
|
13
|
+
],
|
14
|
+
path=tmp_path,
|
15
|
+
local_inputs={0: ("p1",)},
|
16
|
+
overwrite=True,
|
17
|
+
)
|
18
|
+
obj_cache = ObjectCache.build(wk, dependencies=True)
|
19
|
+
assert obj_cache.run_dependencies == {0: set(), 1: {0}, 2: {1}, 3: {2}}
|
20
|
+
assert obj_cache.run_dependents == {0: {1}, 1: {2}, 2: {3}, 3: set()}
|
21
|
+
assert obj_cache.iter_run_dependencies == {0: set(), 1: {0}, 2: {1}, 3: {2}}
|
22
|
+
assert obj_cache.iter_iter_dependencies == {
|
23
|
+
0: set(),
|
24
|
+
1: {0},
|
25
|
+
2: {1},
|
26
|
+
3: {2},
|
27
|
+
}
|
28
|
+
assert obj_cache.elem_iter_dependencies == {
|
29
|
+
0: set(),
|
30
|
+
1: {0},
|
31
|
+
2: {1},
|
32
|
+
3: {2},
|
33
|
+
}
|
34
|
+
assert obj_cache.elem_elem_dependencies == {
|
35
|
+
0: set(),
|
36
|
+
1: {0},
|
37
|
+
2: {1},
|
38
|
+
3: {2},
|
39
|
+
}
|
40
|
+
assert obj_cache.elem_elem_dependents == {0: {1}, 1: {2}, 2: {3}, 3: set()}
|
41
|
+
assert obj_cache.elem_elem_dependents_rec == {
|
42
|
+
0: {1, 2, 3},
|
43
|
+
1: {2, 3},
|
44
|
+
2: {3},
|
45
|
+
3: set(),
|
46
|
+
}
|
hpcflow/tests/unit/test_cli.py
CHANGED
@@ -1,12 +1,145 @@
|
|
1
1
|
import pytest
|
2
2
|
|
3
3
|
from click.testing import CliRunner
|
4
|
+
import click.exceptions
|
4
5
|
|
5
6
|
from hpcflow import __version__
|
6
7
|
from hpcflow.app import app as hf
|
8
|
+
from hpcflow.sdk.cli import ErrorPropagatingClickContext
|
9
|
+
from hpcflow.sdk.cli_common import BoolOrString
|
7
10
|
|
8
11
|
|
9
|
-
def test_version():
|
12
|
+
def test_version() -> None:
|
10
13
|
runner = CliRunner()
|
11
14
|
result = runner.invoke(hf.cli, args="--version")
|
12
15
|
assert result.output.strip() == f"hpcFlow, version {__version__}"
|
16
|
+
|
17
|
+
|
18
|
+
def test_BoolOrString_convert():
|
19
|
+
param_type = BoolOrString(["a"])
|
20
|
+
assert param_type.convert(True, None, None) == True
|
21
|
+
assert param_type.convert(False, None, None) == False
|
22
|
+
assert param_type.convert("yes", None, None) == True
|
23
|
+
assert param_type.convert("no", None, None) == False
|
24
|
+
assert param_type.convert("on", None, None) == True
|
25
|
+
assert param_type.convert("off", None, None) == False
|
26
|
+
assert param_type.convert("a", None, None) == "a"
|
27
|
+
with pytest.raises(click.exceptions.BadParameter):
|
28
|
+
param_type.convert("b", None, None)
|
29
|
+
|
30
|
+
|
31
|
+
def test_error_propagated_with_custom_context_class():
|
32
|
+
class MyException(ValueError):
|
33
|
+
pass
|
34
|
+
|
35
|
+
class MyContextManager:
|
36
|
+
|
37
|
+
# set to True when MyException is raised within this context manager
|
38
|
+
raised = False
|
39
|
+
|
40
|
+
def __enter__(self):
|
41
|
+
return self
|
42
|
+
|
43
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
44
|
+
if exc_type == MyException:
|
45
|
+
self.__class__.raised = True
|
46
|
+
|
47
|
+
@click.group()
|
48
|
+
@click.pass_context
|
49
|
+
def cli(ctx):
|
50
|
+
ctx.with_resource(MyContextManager())
|
51
|
+
|
52
|
+
cli.context_class = ErrorPropagatingClickContext # use custom click Context
|
53
|
+
|
54
|
+
@cli.command()
|
55
|
+
def my_command():
|
56
|
+
raise MyException()
|
57
|
+
|
58
|
+
runner = CliRunner()
|
59
|
+
runner.invoke(cli, args="my-command")
|
60
|
+
|
61
|
+
assert MyContextManager.raised
|
62
|
+
|
63
|
+
|
64
|
+
def test_error_not_propagated_without_custom_context_class():
|
65
|
+
class MyException(ValueError):
|
66
|
+
pass
|
67
|
+
|
68
|
+
class MyContextManager:
|
69
|
+
|
70
|
+
# set to True when MyException is raised within this context manager
|
71
|
+
raised = False
|
72
|
+
|
73
|
+
def __enter__(self):
|
74
|
+
return self
|
75
|
+
|
76
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
77
|
+
if exc_type == MyException:
|
78
|
+
self.__class__.raised = True
|
79
|
+
|
80
|
+
@click.group()
|
81
|
+
@click.pass_context
|
82
|
+
def cli(ctx):
|
83
|
+
ctx.with_resource(MyContextManager())
|
84
|
+
|
85
|
+
@cli.command()
|
86
|
+
def my_command():
|
87
|
+
raise MyException()
|
88
|
+
|
89
|
+
runner = CliRunner()
|
90
|
+
runner.invoke(cli, args="my-command")
|
91
|
+
|
92
|
+
assert not MyContextManager.raised
|
93
|
+
|
94
|
+
|
95
|
+
def test_std_stream_file_created(tmp_path):
|
96
|
+
"""Test exception is intercepted and printed to the specified --std-stream file."""
|
97
|
+
error_file = tmp_path / "std_stream.txt"
|
98
|
+
runner = CliRunner()
|
99
|
+
result = runner.invoke(
|
100
|
+
hf.cli, args=f'--std-stream "{str(error_file)}" internal noop --raise'
|
101
|
+
)
|
102
|
+
assert error_file.is_file()
|
103
|
+
std_stream_contents = error_file.read_text()
|
104
|
+
assert "ValueError: internal noop raised!" in std_stream_contents
|
105
|
+
assert result.exit_code == 1
|
106
|
+
assert result.exc_info[0] == SystemExit
|
107
|
+
|
108
|
+
|
109
|
+
def test_std_stream_file_not_created(null_config, tmp_path):
|
110
|
+
"""Test std stream file is not created when no ouput/errors/exceptions"""
|
111
|
+
error_file = tmp_path / "std_stream.txt"
|
112
|
+
runner = CliRunner()
|
113
|
+
result = runner.invoke(hf.cli, args=f'--std-stream "{str(error_file)}" internal noop')
|
114
|
+
assert not error_file.is_file()
|
115
|
+
assert result.exit_code == 0
|
116
|
+
|
117
|
+
|
118
|
+
def test_cli_exception():
|
119
|
+
"""Test exception is passed to click"""
|
120
|
+
runner = CliRunner()
|
121
|
+
result = runner.invoke(hf.cli, args="internal noop --raise")
|
122
|
+
assert result.exit_code == 1
|
123
|
+
assert result.exc_info[0] == ValueError
|
124
|
+
|
125
|
+
|
126
|
+
def test_cli_click_exit_code_zero(tmp_path):
|
127
|
+
"""Test Click's `Exit` exception is ignored by the `redirect_std_to_file` context manager when the exit code is zero."""
|
128
|
+
error_file = tmp_path / "std_stream.txt"
|
129
|
+
runner = CliRunner()
|
130
|
+
result = runner.invoke(
|
131
|
+
hf.cli, args=f'--std-stream "{str(error_file)}" internal noop --click-exit-code 0'
|
132
|
+
)
|
133
|
+
assert result.exit_code == 0
|
134
|
+
assert not error_file.is_file()
|
135
|
+
|
136
|
+
|
137
|
+
def test_cli_click_exit_code_non_zero(tmp_path):
|
138
|
+
"""Test Click's `Exit` exception is not ignored by the `redirect_std_to_file` context manager when the exit code is non-zero."""
|
139
|
+
error_file = tmp_path / "std_stream.txt"
|
140
|
+
runner = CliRunner()
|
141
|
+
result = runner.invoke(
|
142
|
+
hf.cli, args=f'--std-stream "{str(error_file)}" internal noop --click-exit-code 2'
|
143
|
+
)
|
144
|
+
assert result.exit_code == 2
|
145
|
+
assert error_file.is_file()
|