runem 0.0.32__py3-none-any.whl → 0.1.1__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.
- runem/VERSION +1 -1
- runem/command_line.py +42 -1
- runem/hook_manager.py +5 -1
- runem/job_execute.py +12 -6
- runem/job_wrapper.py +1 -1
- runem/runem.py +63 -48
- runem/types.py +112 -7
- {runem-0.0.32.dist-info → runem-0.1.1.dist-info}/METADATA +8 -7
- {runem-0.0.32.dist-info → runem-0.1.1.dist-info}/RECORD +13 -13
- {runem-0.0.32.dist-info → runem-0.1.1.dist-info}/LICENSE +0 -0
- {runem-0.0.32.dist-info → runem-0.1.1.dist-info}/WHEEL +0 -0
- {runem-0.0.32.dist-info → runem-0.1.1.dist-info}/entry_points.txt +0 -0
- {runem-0.0.32.dist-info → runem-0.1.1.dist-info}/top_level.txt +0 -0
runem/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.1.1
|
runem/command_line.py
CHANGED
@@ -12,6 +12,45 @@ from runem.types import JobNames, OptionConfig, OptionsWritable
|
|
12
12
|
from runem.utils import printable_set
|
13
13
|
|
14
14
|
|
15
|
+
class HelpFormatterFixedWidth(argparse.HelpFormatter):
|
16
|
+
"""This works around test issues via constant width helo output.
|
17
|
+
|
18
|
+
This ensures that we get more constant for help-text by fixing the width to
|
19
|
+
something reasonable.
|
20
|
+
"""
|
21
|
+
|
22
|
+
def __init__(self, prog: typing.Any) -> None:
|
23
|
+
# Override the default width with a fixed width, for tests.
|
24
|
+
super().__init__(
|
25
|
+
prog,
|
26
|
+
# Pretty wide so we do not get wrapping on directories
|
27
|
+
# or process-count output.
|
28
|
+
width=1000,
|
29
|
+
)
|
30
|
+
|
31
|
+
|
32
|
+
def _get_argparse_help_formatter() -> typing.Any:
|
33
|
+
"""Returns a help-formatter for argparse.
|
34
|
+
|
35
|
+
This is for tests only to fake terminals of a constant with when rendering help
|
36
|
+
output.
|
37
|
+
"""
|
38
|
+
# Check environment variable to see if we're in tests and need a fixed width
|
39
|
+
# help output.
|
40
|
+
use_fixed_width = os.getenv("RUNEM_FIXED_HELP_WIDTH", None)
|
41
|
+
|
42
|
+
if use_fixed_width:
|
43
|
+
# Use custom formatter with the width specified in the environment variable
|
44
|
+
return (
|
45
|
+
lambda prog: HelpFormatterFixedWidth( # pylint: disable=unnecessary-lambda
|
46
|
+
prog
|
47
|
+
)
|
48
|
+
)
|
49
|
+
|
50
|
+
# Use default formatter
|
51
|
+
return argparse.HelpFormatter
|
52
|
+
|
53
|
+
|
15
54
|
def parse_args(
|
16
55
|
config_metadata: ConfigMetadata, argv: typing.List[str]
|
17
56
|
) -> ConfigMetadata:
|
@@ -23,7 +62,9 @@ def parse_args(
|
|
23
62
|
Returns the parsed args, the jobs_names_to_run, job_phases_to_run, job_tags_to_run
|
24
63
|
"""
|
25
64
|
parser = argparse.ArgumentParser(
|
26
|
-
add_help=False,
|
65
|
+
add_help=False,
|
66
|
+
description="Runs the Lursight Lang test-suite",
|
67
|
+
formatter_class=_get_argparse_help_formatter(),
|
27
68
|
)
|
28
69
|
parser.add_argument(
|
29
70
|
"-H", "--help", action="help", help="show this help message and exit"
|
runem/hook_manager.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
import typing
|
2
2
|
from collections import defaultdict
|
3
3
|
|
4
|
+
from typing_extensions import Unpack
|
5
|
+
|
4
6
|
from runem.config_metadata import ConfigMetadata
|
5
7
|
from runem.job import Job
|
6
8
|
from runem.job_execute import job_execute
|
@@ -10,6 +12,7 @@ from runem.types import (
|
|
10
12
|
HookConfig,
|
11
13
|
HookName,
|
12
14
|
Hooks,
|
15
|
+
HookSpecificKwargs,
|
13
16
|
HooksStore,
|
14
17
|
JobConfig,
|
15
18
|
)
|
@@ -69,7 +72,7 @@ class HookManager:
|
|
69
72
|
self,
|
70
73
|
hook_name: HookName,
|
71
74
|
config_metadata: ConfigMetadata,
|
72
|
-
**kwargs:
|
75
|
+
**kwargs: Unpack[HookSpecificKwargs],
|
73
76
|
) -> None:
|
74
77
|
"""Invokes all functions registered to a specific hook."""
|
75
78
|
hooks: typing.List[HookConfig] = self.hooks_store.get(hook_name, [])
|
@@ -92,6 +95,7 @@ class HookManager:
|
|
92
95
|
job_execute(
|
93
96
|
job_config,
|
94
97
|
running_jobs={},
|
98
|
+
completed_jobs={},
|
95
99
|
config_metadata=config_metadata,
|
96
100
|
file_lists=file_lists,
|
97
101
|
**kwargs,
|
runem/job_execute.py
CHANGED
@@ -5,13 +5,17 @@ import uuid
|
|
5
5
|
from datetime import timedelta
|
6
6
|
from timeit import default_timer as timer
|
7
7
|
|
8
|
+
from typing_extensions import Unpack
|
9
|
+
|
8
10
|
from runem.config_metadata import ConfigMetadata
|
9
11
|
from runem.informative_dict import ReadOnlyInformativeDict
|
10
12
|
from runem.job import Job
|
11
13
|
from runem.job_wrapper import get_job_wrapper
|
12
14
|
from runem.log import error, log
|
13
15
|
from runem.types import (
|
16
|
+
FilePathList,
|
14
17
|
FilePathListLookup,
|
18
|
+
HookSpecificKwargs,
|
15
19
|
JobConfig,
|
16
20
|
JobFunction,
|
17
21
|
JobReturn,
|
@@ -26,7 +30,7 @@ def job_execute_inner(
|
|
26
30
|
job_config: JobConfig,
|
27
31
|
config_metadata: ConfigMetadata,
|
28
32
|
file_lists: FilePathListLookup,
|
29
|
-
**kwargs:
|
33
|
+
**kwargs: Unpack[HookSpecificKwargs],
|
30
34
|
) -> typing.Tuple[JobTiming, JobReturn]:
|
31
35
|
"""Wrapper for running a job inside a sub-process.
|
32
36
|
|
@@ -36,13 +40,12 @@ def job_execute_inner(
|
|
36
40
|
if config_metadata.args.verbose:
|
37
41
|
log(f"START: '{label}'")
|
38
42
|
root_path: pathlib.Path = config_metadata.cfg_filepath.parent
|
39
|
-
function: JobFunction
|
40
43
|
job_tags: typing.Optional[JobTags] = Job.get_job_tags(job_config)
|
41
44
|
os.chdir(root_path)
|
42
|
-
function = get_job_wrapper(job_config, config_metadata.cfg_filepath)
|
45
|
+
function: JobFunction = get_job_wrapper(job_config, config_metadata.cfg_filepath)
|
43
46
|
|
44
47
|
# get the files for all files found for this job's tags
|
45
|
-
file_list = Job.get_job_files(file_lists, job_tags)
|
48
|
+
file_list: FilePathList = Job.get_job_files(file_lists, job_tags)
|
46
49
|
|
47
50
|
if not file_list:
|
48
51
|
# no files to work on
|
@@ -77,8 +80,9 @@ def job_execute_inner(
|
|
77
80
|
log(f"job: running: '{Job.get_job_name(job_config)}'")
|
78
81
|
reports: JobReturn
|
79
82
|
try:
|
83
|
+
assert isinstance(function, JobFunction)
|
80
84
|
reports = function(
|
81
|
-
options=ReadOnlyInformativeDict(config_metadata.options),
|
85
|
+
options=ReadOnlyInformativeDict(config_metadata.options),
|
82
86
|
file_list=file_list,
|
83
87
|
procs=config_metadata.args.procs,
|
84
88
|
root_path=root_path,
|
@@ -107,9 +111,10 @@ def job_execute_inner(
|
|
107
111
|
def job_execute(
|
108
112
|
job_config: JobConfig,
|
109
113
|
running_jobs: typing.Dict[str, str],
|
114
|
+
completed_jobs: typing.Dict[str, str],
|
110
115
|
config_metadata: ConfigMetadata,
|
111
116
|
file_lists: FilePathListLookup,
|
112
|
-
**kwargs:
|
117
|
+
**kwargs: Unpack[HookSpecificKwargs],
|
113
118
|
) -> typing.Tuple[JobTiming, JobReturn]:
|
114
119
|
"""Thin-wrapper around job_execute_inner needed for mocking in tests.
|
115
120
|
|
@@ -123,5 +128,6 @@ def job_execute(
|
|
123
128
|
file_lists,
|
124
129
|
**kwargs,
|
125
130
|
)
|
131
|
+
completed_jobs[this_id] = running_jobs[this_id]
|
126
132
|
del running_jobs[this_id]
|
127
133
|
return results
|
runem/job_wrapper.py
CHANGED
@@ -18,7 +18,7 @@ def get_job_wrapper(job_wrapper: JobWrapper, cfg_filepath: pathlib.Path) -> JobF
|
|
18
18
|
# validate that the command is "understandable" and usable.
|
19
19
|
command_string: str = job_wrapper["command"]
|
20
20
|
validate_simple_command(command_string)
|
21
|
-
return job_runner_simple_command
|
21
|
+
return job_runner_simple_command
|
22
22
|
|
23
23
|
# if we do not have a simple command address assume we have just an addressed
|
24
24
|
# function
|
runem/runem.py
CHANGED
@@ -28,17 +28,19 @@ import typing
|
|
28
28
|
from collections import defaultdict
|
29
29
|
from datetime import timedelta
|
30
30
|
from itertools import repeat
|
31
|
-
from multiprocessing.managers import DictProxy,
|
31
|
+
from multiprocessing.managers import DictProxy, ValueProxy
|
32
32
|
from timeit import default_timer as timer
|
33
|
+
from types import TracebackType
|
33
34
|
|
34
|
-
from
|
35
|
+
from rich.console import Console, ConsoleOptions, ConsoleRenderable, RenderResult
|
36
|
+
from rich.spinner import Spinner
|
37
|
+
from rich.text import Text
|
35
38
|
|
36
39
|
from runem.command_line import parse_args
|
37
40
|
from runem.config import load_project_config, load_user_configs
|
38
41
|
from runem.config_metadata import ConfigMetadata
|
39
42
|
from runem.config_parse import load_config_metadata
|
40
43
|
from runem.files import find_files
|
41
|
-
from runem.job import Job
|
42
44
|
from runem.job_execute import job_execute
|
43
45
|
from runem.job_filter import filter_jobs
|
44
46
|
from runem.log import error, log, warn
|
@@ -58,6 +60,8 @@ from runem.types import (
|
|
58
60
|
)
|
59
61
|
from runem.utils import printable_set
|
60
62
|
|
63
|
+
rich_console = Console()
|
64
|
+
|
61
65
|
|
62
66
|
def _determine_run_parameters(argv: typing.List[str]) -> ConfigMetadata:
|
63
67
|
"""Loads config, parsing cli input and produces the run config.
|
@@ -85,10 +89,40 @@ def _determine_run_parameters(argv: typing.List[str]) -> ConfigMetadata:
|
|
85
89
|
return config_metadata
|
86
90
|
|
87
91
|
|
92
|
+
class DummySpinner(ConsoleRenderable): # pragma: no cover
|
93
|
+
"""A dummy spinner for when spinners are disabled."""
|
94
|
+
|
95
|
+
def __init__(self) -> None:
|
96
|
+
self.text = ""
|
97
|
+
|
98
|
+
def __rich__(self) -> Text:
|
99
|
+
"""Return a rich Text object for rendering."""
|
100
|
+
return Text(self.text)
|
101
|
+
|
102
|
+
def __rich_console__(
|
103
|
+
self, console: Console, options: ConsoleOptions
|
104
|
+
) -> RenderResult:
|
105
|
+
"""Yield an empty string or placeholder text."""
|
106
|
+
yield Text(self.text)
|
107
|
+
|
108
|
+
def __enter__(self) -> None:
|
109
|
+
"""Support for context manager."""
|
110
|
+
pass
|
111
|
+
|
112
|
+
def __exit__(
|
113
|
+
self,
|
114
|
+
exc_type: typing.Optional[typing.Type[BaseException]],
|
115
|
+
exc_value: typing.Optional[BaseException],
|
116
|
+
traceback: typing.Optional[TracebackType],
|
117
|
+
) -> None:
|
118
|
+
"""Support for context manager."""
|
119
|
+
pass
|
120
|
+
|
121
|
+
|
88
122
|
def _update_progress(
|
89
123
|
label: str,
|
90
124
|
running_jobs: typing.Dict[str, str],
|
91
|
-
|
125
|
+
completed_jobs: typing.Dict[str, str],
|
92
126
|
all_jobs: Jobs,
|
93
127
|
is_running: ValueProxy[bool],
|
94
128
|
num_workers: int,
|
@@ -99,57 +133,37 @@ def _update_progress(
|
|
99
133
|
Args:
|
100
134
|
label (str): The identifier.
|
101
135
|
running_jobs (Dict[str, str]): The currently running jobs.
|
102
|
-
seen_jobs (List[str]): Jobs that the function has previously tracked.
|
103
136
|
all_jobs (Jobs): All jobs, encompassing both completed and running jobs.
|
104
137
|
is_running (ValueProxy[bool]): Flag indicating if jobs are still running.
|
105
138
|
num_workers (int): Indicates the number of workers performing the jobs.
|
106
139
|
"""
|
107
|
-
# Using
|
140
|
+
# Using the `rich` module to show a loading spinner on console
|
141
|
+
spinner: typing.Union[Spinner, DummySpinner]
|
108
142
|
if show_spinner:
|
109
|
-
spinner =
|
110
|
-
|
111
|
-
|
112
|
-
# The set of all job labels, and the set of completed jobs
|
113
|
-
all_job_names: typing.Set[str] = {Job.get_job_name(job) for job in all_jobs}
|
114
|
-
completed_jobs: typing.Set[str] = set()
|
143
|
+
spinner = Spinner("dots", text="Starting tasks...")
|
144
|
+
else:
|
145
|
+
spinner = DummySpinner()
|
115
146
|
|
116
|
-
# This dataset is used to track changes between iterations
|
117
147
|
last_running_jobs_set: typing.Set[str] = set()
|
118
148
|
|
119
|
-
|
120
|
-
|
121
|
-
|
149
|
+
with rich_console.status(spinner):
|
150
|
+
while is_running.value:
|
151
|
+
running_jobs_set: typing.Set[str] = set(running_jobs.values())
|
122
152
|
|
123
|
-
|
124
|
-
|
153
|
+
# Progress report
|
154
|
+
progress: str = f"{len(completed_jobs)}/{len(all_jobs)}"
|
155
|
+
running_jobs_list = printable_set(
|
156
|
+
running_jobs_set
|
157
|
+
) # Reflect current running jobs accurately
|
158
|
+
report: str = f"{label}: {progress}({num_workers}): {running_jobs_list}"
|
159
|
+
if show_spinner:
|
160
|
+
spinner.text = report
|
161
|
+
else:
|
162
|
+
if last_running_jobs_set != running_jobs_set:
|
163
|
+
rich_console.log(report)
|
125
164
|
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
# Check if we're closing to completion
|
130
|
-
workers_retiring: bool = len(remaining_jobs) <= num_workers
|
131
|
-
|
132
|
-
if workers_retiring:
|
133
|
-
# Handle edge case: a task may have disappeared whilst process was sleeping
|
134
|
-
all_completed_jobs: typing.Set[str] = all_job_names - remaining_jobs
|
135
|
-
disappeared_jobs.update(all_completed_jobs - running_jobs_set)
|
136
|
-
|
137
|
-
completed_jobs.update(disappeared_jobs)
|
138
|
-
|
139
|
-
# Prepare progress report
|
140
|
-
progress: str = f"{len(completed_jobs)}/{len(all_jobs)}"
|
141
|
-
running_jobs_list = printable_set(running_jobs_set)
|
142
|
-
if show_spinner:
|
143
|
-
spinner.text = f"{label}: {progress}({num_workers}): {running_jobs_list}"
|
144
|
-
|
145
|
-
# Update the tracked dataset for the next iteration
|
146
|
-
last_running_jobs_set = running_jobs_set
|
147
|
-
|
148
|
-
# Sleep to decrease frequency of updates and reduce CPU usage
|
149
|
-
time.sleep(0.1)
|
150
|
-
|
151
|
-
if show_spinner:
|
152
|
-
spinner.stop()
|
165
|
+
# Sleep for reduced CPU usage
|
166
|
+
time.sleep(0.1)
|
153
167
|
|
154
168
|
|
155
169
|
def _process_jobs(
|
@@ -187,8 +201,8 @@ def _process_jobs(
|
|
187
201
|
subprocess_error: typing.Optional[BaseException] = None
|
188
202
|
|
189
203
|
with multiprocessing.Manager() as manager:
|
190
|
-
seen_jobs: ListProxy[str] = manager.list()
|
191
204
|
running_jobs: DictProxy[typing.Any, typing.Any] = manager.dict()
|
205
|
+
completed_jobs: DictProxy[typing.Any, typing.Any] = manager.dict()
|
192
206
|
is_running: ValueProxy[bool] = manager.Value("b", True)
|
193
207
|
|
194
208
|
terminal_writer_process = multiprocessing.Process(
|
@@ -196,7 +210,7 @@ def _process_jobs(
|
|
196
210
|
args=(
|
197
211
|
phase,
|
198
212
|
running_jobs,
|
199
|
-
|
213
|
+
completed_jobs,
|
200
214
|
jobs,
|
201
215
|
is_running,
|
202
216
|
num_concurrent_procs,
|
@@ -209,10 +223,11 @@ def _process_jobs(
|
|
209
223
|
with multiprocessing.Pool(processes=num_concurrent_procs) as pool:
|
210
224
|
# use starmap so we can pass down the job-configs and the args and the files
|
211
225
|
in_out_job_run_metadatas[phase] = pool.starmap(
|
212
|
-
job_execute,
|
226
|
+
job_execute, # no kwargs passed for jobs here
|
213
227
|
zip(
|
214
228
|
jobs,
|
215
229
|
repeat(running_jobs),
|
230
|
+
repeat(completed_jobs),
|
216
231
|
repeat(config_metadata),
|
217
232
|
repeat(file_lists),
|
218
233
|
),
|
runem/types.py
CHANGED
@@ -1,9 +1,37 @@
|
|
1
|
-
|
1
|
+
"""
|
2
|
+
|
3
|
+
Some note on Unpack and kwargs:
|
4
|
+
We *try* to strongly type `**kwargs` for clarity.
|
5
|
+
We have tried several ways to define a Generic type that encapsulates
|
6
|
+
`**kwargs: SingleType`
|
7
|
+
... but none of the solutions worked with python 3.9 -> 3.12 and mypy 1.9.0,
|
8
|
+
so we have to recommend instead using:
|
9
|
+
`**kwargs: Unpack[KwArgsType]`
|
10
|
+
|
11
|
+
For this to work across versions of python where support for Unpack changes;
|
12
|
+
for example `Unpack` is a python 3.12 feature, but available in the
|
13
|
+
`typing_extensions` module.
|
14
|
+
|
15
|
+
So, for now, it looks like we get away with importing `Unpack` from the
|
16
|
+
`typing_extensions` module, even in python 3.12, so we will use, and
|
17
|
+
recommend using, the `typing_extensions` of `Unpack`, until it becomes
|
18
|
+
obsolete.
|
19
|
+
|
20
|
+
Alternatively, we can use the following, but it's unnecessarily verbose.
|
21
|
+
|
22
|
+
if sys.version_info >= (3, 12): # pragma: no coverage
|
23
|
+
from typing import Unpack
|
24
|
+
else: # pragma: no coverage
|
25
|
+
from typing_extensions import Unpack
|
26
|
+
"""
|
27
|
+
|
2
28
|
import pathlib
|
3
29
|
import typing
|
4
30
|
from datetime import timedelta
|
5
31
|
from enum import Enum
|
6
32
|
|
33
|
+
from typing_extensions import Unpack
|
34
|
+
|
7
35
|
from runem.informative_dict import InformativeDict, ReadOnlyInformativeDict
|
8
36
|
|
9
37
|
|
@@ -96,12 +124,6 @@ FilePathSerialise = str
|
|
96
124
|
FilePathList = typing.List[FilePathSerialise]
|
97
125
|
FilePathListLookup = typing.DefaultDict[JobTag, FilePathList]
|
98
126
|
|
99
|
-
# FIXME: this type is no-longer the actual spec of the test-functions
|
100
|
-
JobFunction = typing.Union[
|
101
|
-
typing.Callable[[argparse.Namespace, OptionsWritable, FilePathList], None],
|
102
|
-
typing.Callable[[typing.Any], None],
|
103
|
-
]
|
104
|
-
|
105
127
|
|
106
128
|
class JobParamConfig(typing.TypedDict):
|
107
129
|
"""Configures what parameters are passed to the test-callable.
|
@@ -250,3 +272,86 @@ ConfigNodes = typing.Union[
|
|
250
272
|
Config = typing.List[ConfigNodes]
|
251
273
|
|
252
274
|
UserConfigMetadata = typing.List[typing.Tuple[Config, pathlib.Path]]
|
275
|
+
|
276
|
+
|
277
|
+
class CommonKwargs(
|
278
|
+
typing.TypedDict,
|
279
|
+
total=True, # each of these are guaranteed to exist in jobs and hooks
|
280
|
+
):
|
281
|
+
"""Defines the base args that are passed to all jobs.
|
282
|
+
|
283
|
+
As we call hooks and job-task in the same manner, this defines the variables that we
|
284
|
+
can access from both hooks and job-tasks.
|
285
|
+
"""
|
286
|
+
|
287
|
+
root_path: pathlib.Path # the path where the .runem.yml file is
|
288
|
+
job: JobConfig # the job or hook task spec ¢ TODO: rename this
|
289
|
+
label: str # the name of the hook or the job-label
|
290
|
+
options: Options # options passed in on the command line
|
291
|
+
procs: int # the max number of concurrent procs to run
|
292
|
+
verbose: bool # control log verbosity
|
293
|
+
|
294
|
+
|
295
|
+
class HookSpecificKwargs(typing.TypedDict, total=False):
|
296
|
+
"""Defines the args that are passed down to the hooks.
|
297
|
+
|
298
|
+
NOTE: that although these however
|
299
|
+
outside of the *hook* context, the data will not be present. Such is the
|
300
|
+
difficulty in dynamic programming.
|
301
|
+
"""
|
302
|
+
|
303
|
+
wall_clock_time_saved: timedelta # only on `HookName.ON_EXIT`
|
304
|
+
|
305
|
+
|
306
|
+
class JobTaskKwargs(
|
307
|
+
typing.TypedDict,
|
308
|
+
total=False, # for now, we don't enforce these types for job-context, but we should.
|
309
|
+
):
|
310
|
+
"""Defines the task-specific args for job-task functions."""
|
311
|
+
|
312
|
+
file_list: FilePathList
|
313
|
+
record_sub_job_time: typing.Optional[typing.Callable[[str, timedelta], None]]
|
314
|
+
|
315
|
+
|
316
|
+
class HookKwargs(CommonKwargs, HookSpecificKwargs):
|
317
|
+
"""A merged set of kwargs for runem-hooks."""
|
318
|
+
|
319
|
+
pass
|
320
|
+
|
321
|
+
|
322
|
+
class JobKwargs(CommonKwargs, JobTaskKwargs):
|
323
|
+
"""A merged set of kwargs for job-tasks."""
|
324
|
+
|
325
|
+
pass
|
326
|
+
|
327
|
+
|
328
|
+
class AllKwargs(CommonKwargs, JobTaskKwargs, HookSpecificKwargs):
|
329
|
+
"""A merged set of kwargs for al job-functions."""
|
330
|
+
|
331
|
+
pass
|
332
|
+
|
333
|
+
|
334
|
+
@typing.runtime_checkable
|
335
|
+
class JobFunction(typing.Protocol):
|
336
|
+
def __call__(self, **kwargs: Unpack[AllKwargs]) -> JobReturn: # pragma: no cover
|
337
|
+
"""Defines the call() protocol's abstract pattern for job-tasks."""
|
338
|
+
|
339
|
+
@property
|
340
|
+
def __name__(self) -> str: # pragma: no cover
|
341
|
+
"""Defines the name protocol for job-task functions.
|
342
|
+
|
343
|
+
This is primarily used for internal tests but can be useful for introspection.
|
344
|
+
"""
|
345
|
+
|
346
|
+
|
347
|
+
def _hook_example(
|
348
|
+
wall_clock_time_saved: timedelta,
|
349
|
+
**kwargs: typing.Any,
|
350
|
+
) -> None:
|
351
|
+
"""An example hook."""
|
352
|
+
|
353
|
+
|
354
|
+
def _job_task_example(
|
355
|
+
**kwargs: Unpack[JobKwargs],
|
356
|
+
) -> None:
|
357
|
+
"""An example job-task function."""
|
@@ -1,17 +1,18 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: runem
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.1.1
|
4
4
|
Summary: Awesome runem created by lursight
|
5
5
|
Home-page: https://github.com/lursight/runem/
|
6
6
|
Author: lursight
|
7
7
|
Description-Content-Type: text/markdown
|
8
8
|
License-File: LICENSE
|
9
|
-
Requires-Dist: halo
|
10
9
|
Requires-Dist: packaging
|
11
10
|
Requires-Dist: PyYAML
|
11
|
+
Requires-Dist: rich
|
12
|
+
Requires-Dist: typing-extensions
|
12
13
|
Provides-Extra: tests
|
13
|
-
Requires-Dist: black==24.
|
14
|
-
Requires-Dist: coverage==7.
|
14
|
+
Requires-Dist: black==24.10.0; extra == "tests"
|
15
|
+
Requires-Dist: coverage==7.5; extra == "tests"
|
15
16
|
Requires-Dist: docformatter==1.7.5; extra == "tests"
|
16
17
|
Requires-Dist: flake8-bugbear==24.2.6; extra == "tests"
|
17
18
|
Requires-Dist: flake8==7.0.0; extra == "tests"
|
@@ -22,10 +23,10 @@ Requires-Dist: mypy==1.9.0; extra == "tests"
|
|
22
23
|
Requires-Dist: pydocstyle==6.3.0; extra == "tests"
|
23
24
|
Requires-Dist: pylint==3.1.0; extra == "tests"
|
24
25
|
Requires-Dist: pylama==8.4.1; extra == "tests"
|
25
|
-
Requires-Dist: pytest-cov==
|
26
|
+
Requires-Dist: pytest-cov==6.0.0; extra == "tests"
|
26
27
|
Requires-Dist: pytest-profiling==1.7.0; extra == "tests"
|
27
|
-
Requires-Dist: pytest-xdist==3.
|
28
|
-
Requires-Dist: pytest==8.
|
28
|
+
Requires-Dist: pytest-xdist==3.6.1; extra == "tests"
|
29
|
+
Requires-Dist: pytest==8.3.3; extra == "tests"
|
29
30
|
Requires-Dist: setuptools; extra == "tests"
|
30
31
|
Requires-Dist: termplotlib==0.3.9; extra == "tests"
|
31
32
|
Requires-Dist: tox; extra == "tests"
|
@@ -1,33 +1,33 @@
|
|
1
|
-
runem/VERSION,sha256
|
1
|
+
runem/VERSION,sha256=gERzFlKfxiAU-6oEf7Z8Uaww4ygwzaNwQFNJ2NZtGWw,6
|
2
2
|
runem/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
runem/__main__.py,sha256=dsOiVZegpfK9JOs5n7UmbX5iwwbj7iFkEbLoVeEgAn4,136
|
4
4
|
runem/base.py,sha256=EZfR7FIlwEdU9Vfe47Wk2DOO8GQqpKxxLNKp6YHueZ4,316
|
5
5
|
runem/blocking_print.py,sha256=S9dtgAeuTzc2-ht-vk9Wl6l-0PwS2tYbHDHDQQitrlA,841
|
6
6
|
runem/cli.py,sha256=wEt_Jnumhl8SiOdKdSJzLkJpWv6n3_Odhi_HeIixr1k,134
|
7
|
-
runem/command_line.py,sha256=
|
7
|
+
runem/command_line.py,sha256=mJbs7uUtAxSnbMxO3C08feOkVusK36x_LIQaeJHivzA,13700
|
8
8
|
runem/config.py,sha256=y-e6j84FDiLSKKw9ShDzRlnS5t2e81MW8fKSKtxtJtg,5935
|
9
9
|
runem/config_metadata.py,sha256=Vy7dx8F-Z5jEp16OP2y6vHHoGkyhoCaTG4KIVkMWR7M,3232
|
10
10
|
runem/config_parse.py,sha256=6mCamzWu7HTotmqFJmLZg9FFE6qe1-rpmo8_v5ESPW8,13401
|
11
11
|
runem/files.py,sha256=QZqPS7OA98lEwlhJNtnaSWlEeTlI8_yn-zjf3QAPoJk,4384
|
12
|
-
runem/hook_manager.py,sha256=
|
12
|
+
runem/hook_manager.py,sha256=8zbMuY4FTSm4kDk2wLBa60zl9NfavnmcOR-qL5jhKTc,4276
|
13
13
|
runem/informative_dict.py,sha256=U7p9z78UwOT4TAfng1iDXCEyeYz6C-XZlx9Z1pWNVrI,1548
|
14
14
|
runem/job.py,sha256=QVXvzz67fJk__-h0womFQsB80-w41E3XRcHpxmRnv3o,2912
|
15
|
-
runem/job_execute.py,sha256=
|
15
|
+
runem/job_execute.py,sha256=eUyvmtjFdJ49AaIOtqJbRXinkF_UFuz20vBGVrYvt0U,4241
|
16
16
|
runem/job_filter.py,sha256=fuxyKCHpTB4HlT_QagBk-IhhmWMlOr9Y9s5voP4yzYU,5370
|
17
17
|
runem/job_runner_simple_command.py,sha256=jxBukPm9bTLNhfQCkqNG5VepvB2ysmWAZwhBPHoTA6o,1091
|
18
|
-
runem/job_wrapper.py,sha256=
|
18
|
+
runem/job_wrapper.py,sha256=H57b52Apcg3q3LyMxus6vNrlv615J4CXiWaQrv8vOgY,936
|
19
19
|
runem/job_wrapper_python.py,sha256=m5xbWQxkDRtawjCcgxctzouv_Pny6bKiG2OPVE1hlgo,4226
|
20
20
|
runem/log.py,sha256=dIrocigvIJs1ZGkAzTogXkAK-0ZW3q5FkjpDgLdeW-E,630
|
21
21
|
runem/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
22
22
|
runem/report.py,sha256=IBCtMgGwnvVbEEqDWbYBGUZmTIzBLnpXqYSK5uu3vgk,8987
|
23
23
|
runem/run_command.py,sha256=Egl_j4bJ9mwi2JEFCsl0W6WH2IRgIdpMN7qdj8voClQ,6386
|
24
|
-
runem/runem.py,sha256=
|
24
|
+
runem/runem.py,sha256=TPBVLiZwEAmKNoQZVHBrrMVgHQfcsJM4uVY9lrrp8X8,13207
|
25
25
|
runem/runem_version.py,sha256=MbETwZO2Tb1Y3hX_OYZjKepEMKA1cjNvr-7Cqhz6e3s,271
|
26
|
-
runem/types.py,sha256
|
26
|
+
runem/types.py,sha256=-RHnskpWEtvqNlrHx3mHrkx6ujEdkCxcU7hu3_lHrJU,10671
|
27
27
|
runem/utils.py,sha256=3N_kel9LsriiMq7kOjT14XhfxUOgz4hdDg97wlLKm3U,221
|
28
|
-
runem-0.
|
29
|
-
runem-0.
|
30
|
-
runem-0.
|
31
|
-
runem-0.
|
32
|
-
runem-0.
|
33
|
-
runem-0.
|
28
|
+
runem-0.1.1.dist-info/LICENSE,sha256=awOCsWJ58m_2kBQwBUGWejVqZm6wuRtCL2hi9rfa0X4,1211
|
29
|
+
runem-0.1.1.dist-info/METADATA,sha256=Eve8gICbiXMPHxQUUtXqB7hU7Dgjq5Gaq-whnLvzCtk,5842
|
30
|
+
runem-0.1.1.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
|
31
|
+
runem-0.1.1.dist-info/entry_points.txt,sha256=nu0g_vBeuPihYtimbtlNusxWovylMppvJ8UxdJlJfvM,46
|
32
|
+
runem-0.1.1.dist-info/top_level.txt,sha256=gK6iqh9OfHDDpErioCC9ul_zx2Q5zWTALtcuGU7Vil4,6
|
33
|
+
runem-0.1.1.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|