runem 0.1.2__tar.gz → 0.3.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.1.2 → runem-0.3.0}/HISTORY.md +50 -0
- {runem-0.1.2 → runem-0.3.0}/PKG-INFO +1 -1
- runem-0.3.0/runem/VERSION +1 -0
- {runem-0.1.2 → runem-0.3.0}/runem/job.py +16 -0
- runem-0.3.0/runem/job_runner_simple_command.py +64 -0
- {runem-0.1.2 → runem-0.3.0}/runem.egg-info/PKG-INFO +1 -1
- {runem-0.1.2 → runem-0.3.0}/tests/test_job.py +19 -1
- runem-0.3.0/tests/test_job_runner_simple_command.py +155 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_runem.py +3 -3
- runem-0.1.2/runem/VERSION +0 -1
- runem-0.1.2/runem/job_runner_simple_command.py +0 -32
- runem-0.1.2/tests/test_job_runner_simple_command.py +0 -39
- {runem-0.1.2 → runem-0.3.0}/Containerfile +0 -0
- {runem-0.1.2 → runem-0.3.0}/LICENSE +0 -0
- {runem-0.1.2 → runem-0.3.0}/MANIFEST.in +0 -0
- {runem-0.1.2 → runem-0.3.0}/README.md +0 -0
- {runem-0.1.2 → runem-0.3.0}/requirements-test.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/requirements.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/__init__.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/__main__.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/base.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/blocking_print.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/cli/initialise_options.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/cli.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/command_line.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/config.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/config_metadata.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/config_parse.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/files.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/hook_manager.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/informative_dict.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/job_execute.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/job_filter.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/job_wrapper.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/job_wrapper_python.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/log.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/py.typed +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/report.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/run_command.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/runem.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/runem_version.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/types/__init__.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/types/common.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/types/errors.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/types/filters.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/types/hooks.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/types/options.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/types/runem_config.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/types/types_jobs.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem/utils.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem.egg-info/SOURCES.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem.egg-info/dependency_links.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem.egg-info/entry_points.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem.egg-info/requires.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/runem.egg-info/top_level.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/setup.cfg +0 -0
- {runem-0.1.2 → runem-0.3.0}/setup.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/__init__.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/cli/test_initialise_options.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/conftest.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/data/help_output.3.10.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/data/help_output.3.11.txt +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/intentional_test_error.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/sanitise_reports_footer.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_base.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_blocking_print.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_cli.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_config.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_config_parse.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_files.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_hook_manager.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_informative_dict.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_job_execute.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_job_filter.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_job_wrapper.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_job_wrapper_python.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_report.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_run_command.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_types/__init__.py +0 -0
- {runem-0.1.2 → runem-0.3.0}/tests/test_types/test_public_api.py +0 -0
@@ -4,6 +4,56 @@ Changelog
|
|
4
4
|
|
5
5
|
(unreleased)
|
6
6
|
------------
|
7
|
+
- Merge pull request #66 from
|
8
|
+
lursight/feat/option_switches_in_simple_commands. [Frank Harrison]
|
9
|
+
|
10
|
+
Feat/option switches in simple commands
|
11
|
+
- Chore(pytest): force colour output in pytest, makes it easier to read.
|
12
|
+
[Frank Harrison]
|
13
|
+
- Feat(option-switches): adds ability to turn on/off switches in simple
|
14
|
+
commands. [Frank Harrison]
|
15
|
+
|
16
|
+
THis is pretty noddy for now, but it means we can do more things like
|
17
|
+
run `black` or `ruff` with `--check` enabled... as well as some other
|
18
|
+
switches.
|
19
|
+
- Merge pull request #65 from lursight/feat/error_on_misplaced_config.
|
20
|
+
[Frank Harrison]
|
21
|
+
|
22
|
+
feat(where-errors): errors when 'tags' and 'phase' are not under 'where'
|
23
|
+
- Feat(where-errors): errors when 'tags' and 'phase' are not under
|
24
|
+
'where' [Frank Harrison]
|
25
|
+
|
26
|
+
|
27
|
+
0.2.0 (2024-11-21)
|
28
|
+
------------------
|
29
|
+
- Release: version 0.2.0 🚀 [Frank Harrison]
|
30
|
+
- Merge pull request #63 from
|
31
|
+
lursight/dependabot/github_actions/actions/cache-4. [Frank Harrison]
|
32
|
+
|
33
|
+
chore(deps): bump actions/cache from 3 to 4
|
34
|
+
- Chore(deps): bump actions/cache from 3 to 4. [dependabot[bot]]
|
35
|
+
|
36
|
+
Bumps [actions/cache](https://github.com/actions/cache) from 3 to 4.
|
37
|
+
- [Release notes](https://github.com/actions/cache/releases)
|
38
|
+
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
|
39
|
+
- [Commits](https://github.com/actions/cache/compare/v3...v4)
|
40
|
+
|
41
|
+
---
|
42
|
+
updated-dependencies:
|
43
|
+
- dependency-name: actions/cache
|
44
|
+
dependency-type: direct:production
|
45
|
+
update-type: version-update:semver-major
|
46
|
+
...
|
47
|
+
- Feat(simple-files): adds ability to pass file-lists to simple-
|
48
|
+
commands. [Frank Harrison]
|
49
|
+
- Feat(simple-files): strongly types the simple job executor. [Frank
|
50
|
+
Harrison]
|
51
|
+
- Feat(simple-files): fixes docstring. [Frank Harrison]
|
52
|
+
|
53
|
+
|
54
|
+
0.1.2 (2024-11-18)
|
55
|
+
------------------
|
56
|
+
- Release: version 0.1.2 🚀 [Frank Harrison]
|
7
57
|
- Chore(actions): simplifies code paths for actions. [Frank Harrison]
|
8
58
|
- Chore(contrib): removes the Makefile commands that have been removed.
|
9
59
|
[Frank Harrison]
|
@@ -0,0 +1 @@
|
|
1
|
+
0.3.0
|
@@ -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
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import shlex
|
2
|
+
import typing
|
3
|
+
|
4
|
+
from typing_extensions import Unpack
|
5
|
+
|
6
|
+
from runem.config_metadata import ConfigMetadata
|
7
|
+
from runem.run_command import run_command
|
8
|
+
from runem.types.common import FilePathList
|
9
|
+
from runem.types.options import OptionsWritable
|
10
|
+
from runem.types.runem_config import JobConfig
|
11
|
+
from runem.types.types_jobs import AllKwargs
|
12
|
+
|
13
|
+
|
14
|
+
def validate_simple_command(command_string: str) -> typing.List[str]:
|
15
|
+
"""Use shlex to handle parsing of the command string, a non-trivial problem."""
|
16
|
+
split_command: typing.List[str] = shlex.split(command_string)
|
17
|
+
return split_command
|
18
|
+
|
19
|
+
|
20
|
+
def job_runner_simple_command(
|
21
|
+
**kwargs: Unpack[AllKwargs],
|
22
|
+
) -> None:
|
23
|
+
"""Parses the command and tries to run it via the system.
|
24
|
+
|
25
|
+
Commands inherit the environment.
|
26
|
+
"""
|
27
|
+
# assume we have the job.command entry, allowing KeyError to propagate up
|
28
|
+
job_config: JobConfig = kwargs["job"]
|
29
|
+
command_string: str = job_config["command"]
|
30
|
+
|
31
|
+
command_string_files: str = command_string
|
32
|
+
if "{file_list}" in command_string:
|
33
|
+
file_list: FilePathList = kwargs["file_list"]
|
34
|
+
file_list_with_quotes: typing.List[str] = [
|
35
|
+
f'"{str(file_path)}"' for file_path in file_list
|
36
|
+
]
|
37
|
+
command_string_files = command_string.replace(
|
38
|
+
"{file_list}", " ".join(file_list_with_quotes)
|
39
|
+
)
|
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
|
+
|
57
|
+
# use shlex to handle parsing of the command string, a non-trivial problem.
|
58
|
+
cmd = validate_simple_command(command_string_options)
|
59
|
+
|
60
|
+
# preserve quotes for consistent handling of strings and avoid the "word
|
61
|
+
# splitting" problem for unix-like shells.
|
62
|
+
cmd_with_quotes = [f'"{token}"' if " " in token else token for token in cmd]
|
63
|
+
|
64
|
+
run_command(cmd=cmd_with_quotes, **kwargs)
|
@@ -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."""
|
@@ -0,0 +1,155 @@
|
|
1
|
+
import io
|
2
|
+
import typing
|
3
|
+
from contextlib import redirect_stdout
|
4
|
+
from pathlib import Path
|
5
|
+
from unittest.mock import ANY, Mock, patch
|
6
|
+
|
7
|
+
from runem.config_metadata import ConfigMetadata
|
8
|
+
from runem.job import Job
|
9
|
+
from runem.job_runner_simple_command import job_runner_simple_command
|
10
|
+
from runem.types.runem_config import JobConfig
|
11
|
+
from tests.test_runem import gen_dummy_config_metadata
|
12
|
+
|
13
|
+
|
14
|
+
@patch(
|
15
|
+
"runem.job_runner_simple_command.run_command",
|
16
|
+
# return_value=None,
|
17
|
+
)
|
18
|
+
def test_job_runner_simple_command(mock_run_command: Mock) -> None:
|
19
|
+
"""Tests the basics of job_runner_simple_command."""
|
20
|
+
job_config: JobConfig = {
|
21
|
+
"command": "echo 'testing job_runner_simple_command'",
|
22
|
+
}
|
23
|
+
config_metadata: ConfigMetadata = gen_dummy_config_metadata()
|
24
|
+
with io.StringIO() as buf, redirect_stdout(buf):
|
25
|
+
ret: None = job_runner_simple_command( # type: ignore[func-returns-value]
|
26
|
+
config_metadata=config_metadata,
|
27
|
+
options=config_metadata.options, # type: ignore
|
28
|
+
file_list=[],
|
29
|
+
procs=config_metadata.args.procs,
|
30
|
+
root_path=Path("."),
|
31
|
+
verbose=True, # config_metadata.args.verbose,
|
32
|
+
# unpack useful data points from the job_config
|
33
|
+
label=Job.get_job_name(job_config),
|
34
|
+
job=job_config,
|
35
|
+
)
|
36
|
+
run_command_stdout = buf.getvalue()
|
37
|
+
|
38
|
+
assert ret is None
|
39
|
+
assert run_command_stdout.split("\n") == [""]
|
40
|
+
mock_run_command.assert_called_once_with(
|
41
|
+
cmd=["echo", '"testing job_runner_simple_command"'],
|
42
|
+
config_metadata=ANY,
|
43
|
+
file_list=[],
|
44
|
+
job={"command": "echo 'testing job_runner_simple_command'"},
|
45
|
+
label="echo 'testing job_runner_simple_command'",
|
46
|
+
options=ANY,
|
47
|
+
procs=1,
|
48
|
+
root_path=Path("."),
|
49
|
+
verbose=True,
|
50
|
+
)
|
51
|
+
|
52
|
+
|
53
|
+
@patch(
|
54
|
+
"runem.job_runner_simple_command.run_command",
|
55
|
+
# return_value=None,
|
56
|
+
)
|
57
|
+
def test_job_runner_simple_command_with_file_list(mock_run_command: Mock) -> None:
|
58
|
+
"""Tests the basics of job_runner_simple_command."""
|
59
|
+
test_cmd_string: str = (
|
60
|
+
'echo "some option before files" {file_list} "some option after files"'
|
61
|
+
)
|
62
|
+
job_config: JobConfig = {
|
63
|
+
"command": test_cmd_string,
|
64
|
+
}
|
65
|
+
config_metadata: ConfigMetadata = gen_dummy_config_metadata()
|
66
|
+
file_list: typing.List[typing.Union[str, Path]] = [
|
67
|
+
Path("file1"),
|
68
|
+
"file2",
|
69
|
+
"file with spaces",
|
70
|
+
]
|
71
|
+
with io.StringIO() as buf, redirect_stdout(buf):
|
72
|
+
ret: None = job_runner_simple_command( # type: ignore[func-returns-value]
|
73
|
+
file_list=file_list, # type: ignore[arg-type] # intentional type mismatch
|
74
|
+
job=job_config,
|
75
|
+
label=Job.get_job_name(job_config),
|
76
|
+
config_metadata=config_metadata,
|
77
|
+
options=config_metadata.options, # type: ignore
|
78
|
+
procs=config_metadata.args.procs,
|
79
|
+
root_path=Path("."),
|
80
|
+
verbose=True, # config_metadata.args.verbose,
|
81
|
+
)
|
82
|
+
run_command_stdout = buf.getvalue()
|
83
|
+
|
84
|
+
assert ret is None
|
85
|
+
assert run_command_stdout.split("\n") == [""]
|
86
|
+
mock_run_command.assert_called_once_with(
|
87
|
+
cmd=[
|
88
|
+
"echo",
|
89
|
+
'"some option before files"',
|
90
|
+
"file1",
|
91
|
+
"file2",
|
92
|
+
'"file with spaces"',
|
93
|
+
'"some option after files"',
|
94
|
+
],
|
95
|
+
config_metadata=ANY,
|
96
|
+
file_list=file_list,
|
97
|
+
job=job_config,
|
98
|
+
label=test_cmd_string,
|
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},
|
152
|
+
procs=1,
|
153
|
+
root_path=Path("."),
|
154
|
+
verbose=True,
|
155
|
+
)
|
@@ -1780,7 +1780,7 @@ def test_progress_updater_with_false(show_spinner: bool) -> None:
|
|
1780
1780
|
)
|
1781
1781
|
|
1782
1782
|
|
1783
|
-
def
|
1783
|
+
def gen_dummy_config_metadata() -> ConfigMetadata:
|
1784
1784
|
config_metadata: ConfigMetadata = ConfigMetadata(
|
1785
1785
|
cfg_filepath=pathlib.Path(__file__),
|
1786
1786
|
phases=("dummy phase 1",),
|
@@ -1803,13 +1803,13 @@ def _dummy_config_metadata() -> ConfigMetadata:
|
|
1803
1803
|
phases_to_run=set(), # JobPhases,
|
1804
1804
|
tags_to_run=set(), # ignored JobTags,
|
1805
1805
|
tags_to_avoid=set(), # ignored JobTags,
|
1806
|
-
options=InformativeDict({}), # Options,
|
1806
|
+
options=InformativeDict({"option_on": True, "option_off": False}), # Options,
|
1807
1807
|
)
|
1808
1808
|
return config_metadata
|
1809
1809
|
|
1810
1810
|
|
1811
1811
|
DUMMY_MAIN_RETURN: MainReturnType = (
|
1812
|
-
|
1812
|
+
gen_dummy_config_metadata(), # ConfigMetadata,
|
1813
1813
|
{}, # job_run_metadatas,
|
1814
1814
|
IntentionalTestError(),
|
1815
1815
|
)
|
runem-0.1.2/runem/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.1.2
|
@@ -1,32 +0,0 @@
|
|
1
|
-
import shlex
|
2
|
-
import typing
|
3
|
-
|
4
|
-
from runem.run_command import run_command
|
5
|
-
from runem.types.runem_config import JobConfig
|
6
|
-
|
7
|
-
|
8
|
-
def validate_simple_command(command_string: str) -> typing.List[str]:
|
9
|
-
# use shlex to handle parsing of the command string, a non-trivial problem.
|
10
|
-
split_command: typing.List[str] = shlex.split(command_string)
|
11
|
-
return split_command
|
12
|
-
|
13
|
-
|
14
|
-
def job_runner_simple_command(
|
15
|
-
**kwargs: typing.Any,
|
16
|
-
) -> None:
|
17
|
-
"""Parses the command and tries to run it via the system.
|
18
|
-
|
19
|
-
Commands inherit the environment.
|
20
|
-
"""
|
21
|
-
# assume we have the job.command entry, allowing KeyError to propagate up
|
22
|
-
job_config: JobConfig = kwargs["job"]
|
23
|
-
command_string: str = job_config["command"]
|
24
|
-
|
25
|
-
# use shlex to handle parsing of the command string, a non-trivial problem.
|
26
|
-
result = validate_simple_command(command_string)
|
27
|
-
|
28
|
-
# preserve quotes for consistent handling of strings and avoid the "word
|
29
|
-
# splitting" problem for unix-like shells.
|
30
|
-
result_with_quotes = [f'"{token}"' if " " in token else token for token in result]
|
31
|
-
|
32
|
-
run_command(cmd=result_with_quotes, **kwargs)
|
@@ -1,39 +0,0 @@
|
|
1
|
-
import io
|
2
|
-
from contextlib import redirect_stdout
|
3
|
-
from unittest.mock import Mock, patch
|
4
|
-
|
5
|
-
from runem.job import Job
|
6
|
-
from runem.job_runner_simple_command import job_runner_simple_command
|
7
|
-
from runem.types.runem_config import JobConfig
|
8
|
-
|
9
|
-
|
10
|
-
@patch(
|
11
|
-
"runem.job_runner_simple_command.run_command",
|
12
|
-
# return_value=None,
|
13
|
-
)
|
14
|
-
def test_job_runner_simple_command(mock_run_command: Mock) -> None:
|
15
|
-
"""Tests the basics of job_runner_simple_command."""
|
16
|
-
job_config: JobConfig = {
|
17
|
-
"command": "echo 'testing job_runner_simple_command'",
|
18
|
-
}
|
19
|
-
with io.StringIO() as buf, redirect_stdout(buf):
|
20
|
-
ret: None = job_runner_simple_command( # type: ignore[func-returns-value]
|
21
|
-
# options=config_metadata.options, # type: ignore
|
22
|
-
# file_list=file_list,
|
23
|
-
# procs=config_metadata.args.procs,
|
24
|
-
# root_path=root_path,
|
25
|
-
verbose=True, # config_metadata.args.verbose,
|
26
|
-
# unpack useful data points from the job_config
|
27
|
-
label=Job.get_job_name(job_config),
|
28
|
-
job=job_config,
|
29
|
-
)
|
30
|
-
run_command_stdout = buf.getvalue()
|
31
|
-
|
32
|
-
assert ret is None
|
33
|
-
assert run_command_stdout.split("\n") == [""]
|
34
|
-
mock_run_command.assert_called_once_with(
|
35
|
-
cmd=["echo", '"testing job_runner_simple_command"'],
|
36
|
-
verbose=True,
|
37
|
-
label="echo 'testing job_runner_simple_command'",
|
38
|
-
job={"command": "echo 'testing job_runner_simple_command'"},
|
39
|
-
)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|