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.
Files changed (79) hide show
  1. {runem-0.2.0 → runem-0.4.0}/HISTORY.md +44 -0
  2. {runem-0.2.0 → runem-0.4.0}/PKG-INFO +1 -1
  3. runem-0.4.0/runem/VERSION +1 -0
  4. runem-0.4.0/runem/blocking_print.py +62 -0
  5. {runem-0.2.0 → runem-0.4.0}/runem/job.py +16 -0
  6. {runem-0.2.0 → runem-0.4.0}/runem/job_runner_simple_command.py +19 -1
  7. {runem-0.2.0 → runem-0.4.0}/runem/runem.py +3 -4
  8. {runem-0.2.0 → runem-0.4.0}/runem.egg-info/PKG-INFO +1 -1
  9. {runem-0.2.0 → runem-0.4.0}/tests/conftest.py +14 -0
  10. {runem-0.2.0 → runem-0.4.0}/tests/test_blocking_print.py +3 -3
  11. {runem-0.2.0 → runem-0.4.0}/tests/test_hook_manager.py +1 -1
  12. {runem-0.2.0 → runem-0.4.0}/tests/test_job.py +19 -1
  13. {runem-0.2.0 → runem-0.4.0}/tests/test_job_runner_simple_command.py +59 -3
  14. {runem-0.2.0 → runem-0.4.0}/tests/test_run_command.py +1 -1
  15. {runem-0.2.0 → runem-0.4.0}/tests/test_runem.py +100 -99
  16. runem-0.2.0/runem/VERSION +0 -1
  17. runem-0.2.0/runem/blocking_print.py +0 -26
  18. {runem-0.2.0 → runem-0.4.0}/Containerfile +0 -0
  19. {runem-0.2.0 → runem-0.4.0}/LICENSE +0 -0
  20. {runem-0.2.0 → runem-0.4.0}/MANIFEST.in +0 -0
  21. {runem-0.2.0 → runem-0.4.0}/README.md +0 -0
  22. {runem-0.2.0 → runem-0.4.0}/requirements-test.txt +0 -0
  23. {runem-0.2.0 → runem-0.4.0}/requirements.txt +0 -0
  24. {runem-0.2.0 → runem-0.4.0}/runem/__init__.py +0 -0
  25. {runem-0.2.0 → runem-0.4.0}/runem/__main__.py +0 -0
  26. {runem-0.2.0 → runem-0.4.0}/runem/base.py +0 -0
  27. {runem-0.2.0 → runem-0.4.0}/runem/cli/initialise_options.py +0 -0
  28. {runem-0.2.0 → runem-0.4.0}/runem/cli.py +0 -0
  29. {runem-0.2.0 → runem-0.4.0}/runem/command_line.py +0 -0
  30. {runem-0.2.0 → runem-0.4.0}/runem/config.py +0 -0
  31. {runem-0.2.0 → runem-0.4.0}/runem/config_metadata.py +0 -0
  32. {runem-0.2.0 → runem-0.4.0}/runem/config_parse.py +0 -0
  33. {runem-0.2.0 → runem-0.4.0}/runem/files.py +0 -0
  34. {runem-0.2.0 → runem-0.4.0}/runem/hook_manager.py +0 -0
  35. {runem-0.2.0 → runem-0.4.0}/runem/informative_dict.py +0 -0
  36. {runem-0.2.0 → runem-0.4.0}/runem/job_execute.py +0 -0
  37. {runem-0.2.0 → runem-0.4.0}/runem/job_filter.py +0 -0
  38. {runem-0.2.0 → runem-0.4.0}/runem/job_wrapper.py +0 -0
  39. {runem-0.2.0 → runem-0.4.0}/runem/job_wrapper_python.py +0 -0
  40. {runem-0.2.0 → runem-0.4.0}/runem/log.py +0 -0
  41. {runem-0.2.0 → runem-0.4.0}/runem/py.typed +0 -0
  42. {runem-0.2.0 → runem-0.4.0}/runem/report.py +0 -0
  43. {runem-0.2.0 → runem-0.4.0}/runem/run_command.py +0 -0
  44. {runem-0.2.0 → runem-0.4.0}/runem/runem_version.py +0 -0
  45. {runem-0.2.0 → runem-0.4.0}/runem/types/__init__.py +0 -0
  46. {runem-0.2.0 → runem-0.4.0}/runem/types/common.py +0 -0
  47. {runem-0.2.0 → runem-0.4.0}/runem/types/errors.py +0 -0
  48. {runem-0.2.0 → runem-0.4.0}/runem/types/filters.py +0 -0
  49. {runem-0.2.0 → runem-0.4.0}/runem/types/hooks.py +0 -0
  50. {runem-0.2.0 → runem-0.4.0}/runem/types/options.py +0 -0
  51. {runem-0.2.0 → runem-0.4.0}/runem/types/runem_config.py +0 -0
  52. {runem-0.2.0 → runem-0.4.0}/runem/types/types_jobs.py +0 -0
  53. {runem-0.2.0 → runem-0.4.0}/runem/utils.py +0 -0
  54. {runem-0.2.0 → runem-0.4.0}/runem.egg-info/SOURCES.txt +0 -0
  55. {runem-0.2.0 → runem-0.4.0}/runem.egg-info/dependency_links.txt +0 -0
  56. {runem-0.2.0 → runem-0.4.0}/runem.egg-info/entry_points.txt +0 -0
  57. {runem-0.2.0 → runem-0.4.0}/runem.egg-info/requires.txt +0 -0
  58. {runem-0.2.0 → runem-0.4.0}/runem.egg-info/top_level.txt +0 -0
  59. {runem-0.2.0 → runem-0.4.0}/setup.cfg +0 -0
  60. {runem-0.2.0 → runem-0.4.0}/setup.py +0 -0
  61. {runem-0.2.0 → runem-0.4.0}/tests/__init__.py +0 -0
  62. {runem-0.2.0 → runem-0.4.0}/tests/cli/test_initialise_options.py +0 -0
  63. {runem-0.2.0 → runem-0.4.0}/tests/data/help_output.3.10.txt +0 -0
  64. {runem-0.2.0 → runem-0.4.0}/tests/data/help_output.3.11.txt +0 -0
  65. {runem-0.2.0 → runem-0.4.0}/tests/intentional_test_error.py +0 -0
  66. {runem-0.2.0 → runem-0.4.0}/tests/sanitise_reports_footer.py +0 -0
  67. {runem-0.2.0 → runem-0.4.0}/tests/test_base.py +0 -0
  68. {runem-0.2.0 → runem-0.4.0}/tests/test_cli.py +0 -0
  69. {runem-0.2.0 → runem-0.4.0}/tests/test_config.py +0 -0
  70. {runem-0.2.0 → runem-0.4.0}/tests/test_config_parse.py +0 -0
  71. {runem-0.2.0 → runem-0.4.0}/tests/test_files.py +0 -0
  72. {runem-0.2.0 → runem-0.4.0}/tests/test_informative_dict.py +0 -0
  73. {runem-0.2.0 → runem-0.4.0}/tests/test_job_execute.py +0 -0
  74. {runem-0.2.0 → runem-0.4.0}/tests/test_job_filter.py +0 -0
  75. {runem-0.2.0 → runem-0.4.0}/tests/test_job_wrapper.py +0 -0
  76. {runem-0.2.0 → runem-0.4.0}/tests/test_job_wrapper_python.py +0 -0
  77. {runem-0.2.0 → runem-0.4.0}/tests/test_report.py +0 -0
  78. {runem-0.2.0 → runem-0.4.0}/tests/test_types/__init__.py +0 -0
  79. {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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: runem
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Awesome runem created by lursight
5
5
  Home-page: https://github.com/lursight/runem/
6
6
  Author: lursight
@@ -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(command_string_files)
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 rich_console.status(spinner):
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
- rich_console.log(report)
159
+ RICH_CONSOLE.log(report)
161
160
 
162
161
  # Sleep for reduced CPU usage
163
162
  time.sleep(0.1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: runem
3
- Version: 0.2.0
3
+ Version: 0.4.0
4
4
  Summary: Awesome runem created by lursight
5
5
  Home-page: https://github.com/lursight/runem/
6
6
  Author: lursight
@@ -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=None)
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=None)
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:\tinitialising 2 hooks for 'HookName.ON_EXIT'",
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: allowed return ids are: 3\n"
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
  )