runem 0.2.0__tar.gz → 0.4.0__tar.gz
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.
- {runem-0.2.0 → runem-0.4.0}/HISTORY.md +44 -0
- {runem-0.2.0 → runem-0.4.0}/PKG-INFO +1 -1
- runem-0.4.0/runem/VERSION +1 -0
- runem-0.4.0/runem/blocking_print.py +62 -0
- {runem-0.2.0 → runem-0.4.0}/runem/job.py +16 -0
- {runem-0.2.0 → runem-0.4.0}/runem/job_runner_simple_command.py +19 -1
- {runem-0.2.0 → runem-0.4.0}/runem/runem.py +3 -4
- {runem-0.2.0 → runem-0.4.0}/runem.egg-info/PKG-INFO +1 -1
- {runem-0.2.0 → runem-0.4.0}/tests/conftest.py +14 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_blocking_print.py +3 -3
- {runem-0.2.0 → runem-0.4.0}/tests/test_hook_manager.py +1 -1
- {runem-0.2.0 → runem-0.4.0}/tests/test_job.py +19 -1
- {runem-0.2.0 → runem-0.4.0}/tests/test_job_runner_simple_command.py +59 -3
- {runem-0.2.0 → runem-0.4.0}/tests/test_run_command.py +1 -1
- {runem-0.2.0 → runem-0.4.0}/tests/test_runem.py +100 -99
- runem-0.2.0/runem/VERSION +0 -1
- runem-0.2.0/runem/blocking_print.py +0 -26
- {runem-0.2.0 → runem-0.4.0}/Containerfile +0 -0
- {runem-0.2.0 → runem-0.4.0}/LICENSE +0 -0
- {runem-0.2.0 → runem-0.4.0}/MANIFEST.in +0 -0
- {runem-0.2.0 → runem-0.4.0}/README.md +0 -0
- {runem-0.2.0 → runem-0.4.0}/requirements-test.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/requirements.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/__init__.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/__main__.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/base.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/cli/initialise_options.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/cli.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/command_line.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/config.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/config_metadata.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/config_parse.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/files.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/hook_manager.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/informative_dict.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/job_execute.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/job_filter.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/job_wrapper.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/job_wrapper_python.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/log.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/py.typed +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/report.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/run_command.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/runem_version.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/types/__init__.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/types/common.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/types/errors.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/types/filters.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/types/hooks.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/types/options.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/types/runem_config.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/types/types_jobs.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem/utils.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem.egg-info/SOURCES.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem.egg-info/dependency_links.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem.egg-info/entry_points.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem.egg-info/requires.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/runem.egg-info/top_level.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/setup.cfg +0 -0
- {runem-0.2.0 → runem-0.4.0}/setup.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/__init__.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/cli/test_initialise_options.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/data/help_output.3.10.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/data/help_output.3.11.txt +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/intentional_test_error.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/sanitise_reports_footer.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_base.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_cli.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_config.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_config_parse.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_files.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_informative_dict.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_job_execute.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_job_filter.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_job_wrapper.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_job_wrapper_python.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_report.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_types/__init__.py +0 -0
- {runem-0.2.0 → runem-0.4.0}/tests/test_types/test_public_api.py +0 -0
@@ -4,6 +4,50 @@ Changelog
|
|
4
4
|
|
5
5
|
(unreleased)
|
6
6
|
------------
|
7
|
+
- Merge pull request #67 from lursight/feat/prettier_logging. [Frank
|
8
|
+
Harrison]
|
9
|
+
|
10
|
+
feat(rich): prettier logging with rich
|
11
|
+
- Feat(rich): disable markup handling. [Frank Harrison]
|
12
|
+
|
13
|
+
We got errors running logging through rich in CiCd where
|
14
|
+
[/path/to/thing] was being seen as rich markup, specifically as a rich
|
15
|
+
markup close-tag.
|
16
|
+
|
17
|
+
This stops parsing strings as markup and therefore works around the
|
18
|
+
error.
|
19
|
+
- Feat(rich): prettier logging with rich. [Frank Harrison]
|
20
|
+
|
21
|
+
It also makes the logging more useful in different contexts by removing
|
22
|
+
tabs and so on.
|
23
|
+
|
24
|
+
|
25
|
+
0.3.0 (2024-12-03)
|
26
|
+
------------------
|
27
|
+
- Release: version 0.3.0 🚀 [Frank Harrison]
|
28
|
+
- Merge pull request #66 from
|
29
|
+
lursight/feat/option_switches_in_simple_commands. [Frank Harrison]
|
30
|
+
|
31
|
+
Feat/option switches in simple commands
|
32
|
+
- Chore(pytest): force colour output in pytest, makes it easier to read.
|
33
|
+
[Frank Harrison]
|
34
|
+
- Feat(option-switches): adds ability to turn on/off switches in simple
|
35
|
+
commands. [Frank Harrison]
|
36
|
+
|
37
|
+
THis is pretty noddy for now, but it means we can do more things like
|
38
|
+
run `black` or `ruff` with `--check` enabled... as well as some other
|
39
|
+
switches.
|
40
|
+
- Merge pull request #65 from lursight/feat/error_on_misplaced_config.
|
41
|
+
[Frank Harrison]
|
42
|
+
|
43
|
+
feat(where-errors): errors when 'tags' and 'phase' are not under 'where'
|
44
|
+
- Feat(where-errors): errors when 'tags' and 'phase' are not under
|
45
|
+
'where' [Frank Harrison]
|
46
|
+
|
47
|
+
|
48
|
+
0.2.0 (2024-11-21)
|
49
|
+
------------------
|
50
|
+
- Release: version 0.2.0 🚀 [Frank Harrison]
|
7
51
|
- Merge pull request #63 from
|
8
52
|
lursight/dependabot/github_actions/actions/cache-4. [Frank Harrison]
|
9
53
|
|
@@ -0,0 +1 @@
|
|
1
|
+
0.4.0
|
@@ -0,0 +1,62 @@
|
|
1
|
+
import time
|
2
|
+
import typing
|
3
|
+
|
4
|
+
from rich.console import Console
|
5
|
+
|
6
|
+
|
7
|
+
def _reset_console() -> Console:
|
8
|
+
"""Sets the nice user-facing console.
|
9
|
+
|
10
|
+
This function exists so we can reset the output from tests.
|
11
|
+
"""
|
12
|
+
global RICH_CONSOLE # pylint: disable=global-statement
|
13
|
+
|
14
|
+
RICH_CONSOLE = Console(
|
15
|
+
log_path=False, # Do NOT print the source path.
|
16
|
+
markup=False, # Do NOT print markup e.g. `[blink]Don't Panic![/blink]`.
|
17
|
+
)
|
18
|
+
return RICH_CONSOLE
|
19
|
+
|
20
|
+
|
21
|
+
# Overridden in tests.
|
22
|
+
RICH_CONSOLE: Console = _reset_console()
|
23
|
+
|
24
|
+
|
25
|
+
def _reset_console_for_tests() -> None:
|
26
|
+
"""Overrides the console output with more deterministic version.
|
27
|
+
|
28
|
+
... so we can have deterministic tests.
|
29
|
+
"""
|
30
|
+
global RICH_CONSOLE # pylint: disable=global-statement
|
31
|
+
RICH_CONSOLE = Console(
|
32
|
+
log_path=False, # Do NOT print the source path.
|
33
|
+
log_time=False, # Do not prefix with log time e.g. `[time] log message`.
|
34
|
+
markup=False, # Do NOT print markup e.g. `[blink]Don't Panic![/blink]`.
|
35
|
+
width=999, # A very wide width.
|
36
|
+
)
|
37
|
+
|
38
|
+
|
39
|
+
def blocking_print(
|
40
|
+
msg: str = "",
|
41
|
+
end: typing.Optional[str] = None,
|
42
|
+
max_retries: int = 5,
|
43
|
+
sleep_time_s: float = 0.1,
|
44
|
+
) -> None:
|
45
|
+
"""Attempt to print a message, retrying on BlockingIOError.
|
46
|
+
|
47
|
+
Sometimes in long-lasting jobs, that produce lots of output, we hit
|
48
|
+
BlockingIOError where we can't print to screen because the buffer is full or
|
49
|
+
already being written to (for example), i.e. the `print` would need to be a
|
50
|
+
'blocking' call, which it is not.
|
51
|
+
"""
|
52
|
+
if end is None:
|
53
|
+
end = "\n"
|
54
|
+
for _ in range(max_retries):
|
55
|
+
try:
|
56
|
+
RICH_CONSOLE.print(msg, end=end)
|
57
|
+
break # Success, exit the retry loop
|
58
|
+
except BlockingIOError:
|
59
|
+
time.sleep(sleep_time_s) # Wait a bit for the buffer to clear
|
60
|
+
else:
|
61
|
+
# Optional: handle the failure to print after all retries
|
62
|
+
pass
|
@@ -11,6 +11,12 @@ class NoJobName(ValueError):
|
|
11
11
|
pass
|
12
12
|
|
13
13
|
|
14
|
+
class BadWhenConfigLocation(ValueError):
|
15
|
+
"""The job-config does not contain a label and can't be coerced to crate one."""
|
16
|
+
|
17
|
+
pass
|
18
|
+
|
19
|
+
|
14
20
|
class Job:
|
15
21
|
"""A class with utility functions for jobs.
|
16
22
|
|
@@ -27,6 +33,16 @@ class Job:
|
|
27
33
|
|
28
34
|
TODO: make a non-static member function
|
29
35
|
"""
|
36
|
+
if "tags" in job:
|
37
|
+
raise BadWhenConfigLocation(
|
38
|
+
"'tags' should be listed inside the 'when' config for jobs"
|
39
|
+
)
|
40
|
+
|
41
|
+
if "phase" in job:
|
42
|
+
raise BadWhenConfigLocation(
|
43
|
+
"'phase' should be listed inside the 'when' config for jobs"
|
44
|
+
)
|
45
|
+
|
30
46
|
if "when" not in job or "tags" not in job["when"]:
|
31
47
|
# handle the special case where we have No tags
|
32
48
|
return None
|
@@ -3,8 +3,10 @@ import typing
|
|
3
3
|
|
4
4
|
from typing_extensions import Unpack
|
5
5
|
|
6
|
+
from runem.config_metadata import ConfigMetadata
|
6
7
|
from runem.run_command import run_command
|
7
8
|
from runem.types.common import FilePathList
|
9
|
+
from runem.types.options import OptionsWritable
|
8
10
|
from runem.types.runem_config import JobConfig
|
9
11
|
from runem.types.types_jobs import AllKwargs
|
10
12
|
|
@@ -36,8 +38,24 @@ def job_runner_simple_command(
|
|
36
38
|
"{file_list}", " ".join(file_list_with_quotes)
|
37
39
|
)
|
38
40
|
|
41
|
+
config_metadata: ConfigMetadata = kwargs["config_metadata"]
|
42
|
+
options: OptionsWritable = config_metadata.options
|
43
|
+
command_string_options: str = command_string_files
|
44
|
+
for name, value in options.items():
|
45
|
+
# For now, just pass `--option-name`, `--check` or similar to the
|
46
|
+
# command line. At some point we will want this to be cleverer, but
|
47
|
+
# this will do for now.
|
48
|
+
option_search = f"{{{name}}}"
|
49
|
+
if option_search in command_string_files:
|
50
|
+
replacement = ""
|
51
|
+
if value:
|
52
|
+
replacement = f"--{name}"
|
53
|
+
command_string_options = command_string_options.replace(
|
54
|
+
option_search, replacement
|
55
|
+
)
|
56
|
+
|
39
57
|
# use shlex to handle parsing of the command string, a non-trivial problem.
|
40
|
-
cmd = validate_simple_command(
|
58
|
+
cmd = validate_simple_command(command_string_options)
|
41
59
|
|
42
60
|
# preserve quotes for consistent handling of strings and avoid the "word
|
43
61
|
# splitting" problem for unix-like shells.
|
@@ -36,6 +36,7 @@ from rich.console import Console, ConsoleOptions, ConsoleRenderable, RenderResul
|
|
36
36
|
from rich.spinner import Spinner
|
37
37
|
from rich.text import Text
|
38
38
|
|
39
|
+
from runem.blocking_print import RICH_CONSOLE
|
39
40
|
from runem.command_line import parse_args
|
40
41
|
from runem.config import load_project_config, load_user_configs
|
41
42
|
from runem.config_metadata import ConfigMetadata
|
@@ -57,8 +58,6 @@ from runem.types.types_jobs import (
|
|
57
58
|
)
|
58
59
|
from runem.utils import printable_set
|
59
60
|
|
60
|
-
rich_console = Console()
|
61
|
-
|
62
61
|
|
63
62
|
def _determine_run_parameters(argv: typing.List[str]) -> ConfigMetadata:
|
64
63
|
"""Loads config, parsing cli input and produces the run config.
|
@@ -143,7 +142,7 @@ def _update_progress(
|
|
143
142
|
|
144
143
|
last_running_jobs_set: typing.Set[str] = set()
|
145
144
|
|
146
|
-
with
|
145
|
+
with RICH_CONSOLE.status(spinner):
|
147
146
|
while is_running.value:
|
148
147
|
running_jobs_set: typing.Set[str] = set(running_jobs.values())
|
149
148
|
|
@@ -157,7 +156,7 @@ def _update_progress(
|
|
157
156
|
spinner.text = report
|
158
157
|
else:
|
159
158
|
if last_running_jobs_set != running_jobs_set:
|
160
|
-
|
159
|
+
RICH_CONSOLE.log(report)
|
161
160
|
|
162
161
|
# Sleep for reduced CPU usage
|
163
162
|
time.sleep(0.1)
|
@@ -5,9 +5,23 @@ import typing
|
|
5
5
|
|
6
6
|
import pytest
|
7
7
|
|
8
|
+
from runem.blocking_print import _reset_console, _reset_console_for_tests
|
9
|
+
|
8
10
|
FixtureRequest = typing.Any
|
9
11
|
|
10
12
|
|
13
|
+
@pytest.fixture(autouse=True)
|
14
|
+
def use_test_rich_print(request: FixtureRequest) -> typing.Generator[None, None, None]:
|
15
|
+
"""Each test should use the test-version of the `rich` print function.
|
16
|
+
|
17
|
+
This is so we get deterministic output with out timestamps nor auto-wrapping console
|
18
|
+
output.
|
19
|
+
"""
|
20
|
+
_reset_console_for_tests()
|
21
|
+
yield
|
22
|
+
_reset_console()
|
23
|
+
|
24
|
+
|
11
25
|
# each test runs on cwd to its temp dir
|
12
26
|
@pytest.fixture(autouse=True)
|
13
27
|
def go_to_tmp_path(request: FixtureRequest) -> typing.Generator[None, None, None]:
|
@@ -8,7 +8,7 @@ from runem import blocking_print
|
|
8
8
|
|
9
9
|
@pytest.fixture(name="mock_print")
|
10
10
|
def mock_print_fixture() -> typing.Generator[MagicMock, None, None]:
|
11
|
-
with patch("runem.blocking_print.print") as mock_print:
|
11
|
+
with patch("runem.blocking_print.RICH_CONSOLE.print") as mock_print:
|
12
12
|
yield mock_print
|
13
13
|
|
14
14
|
|
@@ -23,7 +23,7 @@ def test_blocking_print_success_first_try(
|
|
23
23
|
) -> None:
|
24
24
|
"""Test that blocking_print prints the message successfully on the first try."""
|
25
25
|
blocking_print.blocking_print("Test message")
|
26
|
-
mock_print.assert_called_once_with("Test message", end=
|
26
|
+
mock_print.assert_called_once_with("Test message", end="\n")
|
27
27
|
mock_sleep.assert_not_called()
|
28
28
|
|
29
29
|
|
@@ -57,7 +57,7 @@ def test_blocking_print_empty_message(
|
|
57
57
|
) -> None:
|
58
58
|
"""Test that blocking_print handles an empty message."""
|
59
59
|
blocking_print.blocking_print()
|
60
|
-
mock_print.assert_called_once_with("", end=
|
60
|
+
mock_print.assert_called_once_with("", end="\n")
|
61
61
|
mock_sleep.assert_not_called()
|
62
62
|
|
63
63
|
|
@@ -149,7 +149,7 @@ def test_invoke_hooks(do_hooks: bool, hook_man: HookManager) -> None:
|
|
149
149
|
if do_hooks:
|
150
150
|
expected_stdout = [
|
151
151
|
"runem: hooks: initialising 2 hooks",
|
152
|
-
"runem: hooks
|
152
|
+
"runem: hooks: initialising 2 hooks for 'HookName.ON_EXIT'",
|
153
153
|
(
|
154
154
|
"runem: hooks: registered hook for 'HookName.ON_EXIT', have 1: "
|
155
155
|
"<THIS_FILE>._dummy_hook"
|
@@ -3,7 +3,7 @@ from collections import defaultdict
|
|
3
3
|
|
4
4
|
import pytest
|
5
5
|
|
6
|
-
from runem.job import Job, NoJobName
|
6
|
+
from runem.job import BadWhenConfigLocation, Job, NoJobName
|
7
7
|
from runem.types.common import FilePathList, JobTags
|
8
8
|
from runem.types.filters import FilePathListLookup
|
9
9
|
from runem.types.runem_config import JobConfig
|
@@ -27,6 +27,24 @@ def test_get_job_tags(
|
|
27
27
|
assert result == expected_result
|
28
28
|
|
29
29
|
|
30
|
+
def test_get_job_tags_bad_tags_path() -> None:
|
31
|
+
"""Tests that the 'tags' entry is on 'when' and not on root."""
|
32
|
+
job_config: JobConfig = { # type: ignore[typeddict-unknown-key]
|
33
|
+
"tags": ["dummy tags"],
|
34
|
+
}
|
35
|
+
with pytest.raises(BadWhenConfigLocation):
|
36
|
+
Job.get_job_tags(job_config)
|
37
|
+
|
38
|
+
|
39
|
+
def test_get_job_tags_bad_phase_path() -> None:
|
40
|
+
"""Tests that the 'phase' entry is on 'when' and not on root."""
|
41
|
+
job_config: JobConfig = { # type: ignore[typeddict-unknown-key]
|
42
|
+
"phase": "dummy tags",
|
43
|
+
}
|
44
|
+
with pytest.raises(BadWhenConfigLocation):
|
45
|
+
Job.get_job_tags(job_config)
|
46
|
+
|
47
|
+
|
30
48
|
@pytest.fixture(name="file_lists")
|
31
49
|
def file_lists_fixture() -> FilePathListLookup:
|
32
50
|
"""Define a sample file_lists dictionary for testing."""
|
@@ -2,7 +2,7 @@ import io
|
|
2
2
|
import typing
|
3
3
|
from contextlib import redirect_stdout
|
4
4
|
from pathlib import Path
|
5
|
-
from unittest.mock import Mock, patch
|
5
|
+
from unittest.mock import ANY, Mock, patch
|
6
6
|
|
7
7
|
from runem.config_metadata import ConfigMetadata
|
8
8
|
from runem.job import Job
|
@@ -23,6 +23,7 @@ def test_job_runner_simple_command(mock_run_command: Mock) -> None:
|
|
23
23
|
config_metadata: ConfigMetadata = gen_dummy_config_metadata()
|
24
24
|
with io.StringIO() as buf, redirect_stdout(buf):
|
25
25
|
ret: None = job_runner_simple_command( # type: ignore[func-returns-value]
|
26
|
+
config_metadata=config_metadata,
|
26
27
|
options=config_metadata.options, # type: ignore
|
27
28
|
file_list=[],
|
28
29
|
procs=config_metadata.args.procs,
|
@@ -38,10 +39,11 @@ def test_job_runner_simple_command(mock_run_command: Mock) -> None:
|
|
38
39
|
assert run_command_stdout.split("\n") == [""]
|
39
40
|
mock_run_command.assert_called_once_with(
|
40
41
|
cmd=["echo", '"testing job_runner_simple_command"'],
|
42
|
+
config_metadata=ANY,
|
41
43
|
file_list=[],
|
42
44
|
job={"command": "echo 'testing job_runner_simple_command'"},
|
43
45
|
label="echo 'testing job_runner_simple_command'",
|
44
|
-
options=
|
46
|
+
options=ANY,
|
45
47
|
procs=1,
|
46
48
|
root_path=Path("."),
|
47
49
|
verbose=True,
|
@@ -71,6 +73,7 @@ def test_job_runner_simple_command_with_file_list(mock_run_command: Mock) -> Non
|
|
71
73
|
file_list=file_list, # type: ignore[arg-type] # intentional type mismatch
|
72
74
|
job=job_config,
|
73
75
|
label=Job.get_job_name(job_config),
|
76
|
+
config_metadata=config_metadata,
|
74
77
|
options=config_metadata.options, # type: ignore
|
75
78
|
procs=config_metadata.args.procs,
|
76
79
|
root_path=Path("."),
|
@@ -89,10 +92,63 @@ def test_job_runner_simple_command_with_file_list(mock_run_command: Mock) -> Non
|
|
89
92
|
'"file with spaces"',
|
90
93
|
'"some option after files"',
|
91
94
|
],
|
95
|
+
config_metadata=ANY,
|
92
96
|
file_list=file_list,
|
93
97
|
job=job_config,
|
94
98
|
label=test_cmd_string,
|
95
|
-
options=
|
99
|
+
options=ANY,
|
100
|
+
procs=1,
|
101
|
+
root_path=Path("."),
|
102
|
+
verbose=True,
|
103
|
+
)
|
104
|
+
|
105
|
+
|
106
|
+
@patch(
|
107
|
+
"runem.job_runner_simple_command.run_command",
|
108
|
+
# return_value=None,
|
109
|
+
)
|
110
|
+
def test_job_runner_simple_command_with_option(mock_run_command: Mock) -> None:
|
111
|
+
"""Tests that option-passing to jobs, pass --option_on but not --option_off."""
|
112
|
+
test_cmd_string: str = (
|
113
|
+
'echo "some option before switch" {option_on} {option_off} "some option after switch"'
|
114
|
+
)
|
115
|
+
job_config: JobConfig = {
|
116
|
+
"command": test_cmd_string,
|
117
|
+
}
|
118
|
+
config_metadata: ConfigMetadata = gen_dummy_config_metadata()
|
119
|
+
file_list: typing.List[typing.Union[str, Path]] = [
|
120
|
+
Path("file1"),
|
121
|
+
"file2",
|
122
|
+
"file with spaces",
|
123
|
+
]
|
124
|
+
with io.StringIO() as buf, redirect_stdout(buf):
|
125
|
+
ret: None = job_runner_simple_command( # type: ignore[func-returns-value]
|
126
|
+
file_list=file_list, # type: ignore[arg-type] # intentional type mismatch
|
127
|
+
job=job_config,
|
128
|
+
label=Job.get_job_name(job_config),
|
129
|
+
config_metadata=config_metadata,
|
130
|
+
options=config_metadata.options, # type: ignore
|
131
|
+
procs=config_metadata.args.procs,
|
132
|
+
root_path=Path("."),
|
133
|
+
verbose=True, # config_metadata.args.verbose,
|
134
|
+
)
|
135
|
+
run_command_stdout = buf.getvalue()
|
136
|
+
|
137
|
+
assert ret is None
|
138
|
+
assert run_command_stdout.split("\n") == [""]
|
139
|
+
mock_run_command.assert_called_once_with(
|
140
|
+
cmd=[
|
141
|
+
"echo",
|
142
|
+
'"some option before switch"',
|
143
|
+
"--option_on",
|
144
|
+
# Not this -> "--option_off",
|
145
|
+
'"some option after switch"',
|
146
|
+
],
|
147
|
+
config_metadata=ANY,
|
148
|
+
file_list=file_list,
|
149
|
+
job=job_config,
|
150
|
+
label=test_cmd_string,
|
151
|
+
options={"option_on": True, "option_off": False},
|
96
152
|
procs=1,
|
97
153
|
root_path=Path("."),
|
98
154
|
verbose=True,
|
@@ -300,7 +300,7 @@ def test_run_command_basic_call_non_standard_exit_ok_code_verbose(
|
|
300
300
|
# check the log output hasn't changed. Update as needed.
|
301
301
|
assert run_command_stdout == (
|
302
302
|
"runem: running: start: test command: ls\n"
|
303
|
-
"runem:
|
303
|
+
"runem: allowed return ids are: 3\n"
|
304
304
|
"runem: test command: test output\n"
|
305
305
|
"runem: running: done: test command: ls\n"
|
306
306
|
)
|