runem 0.1.0__tar.gz → 0.1.1__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.0 → runem-0.1.1}/HISTORY.md +21 -0
- {runem-0.1.0 → runem-0.1.1}/PKG-INFO +1 -1
- runem-0.1.1/runem/VERSION +1 -0
- {runem-0.1.0 → runem-0.1.1}/runem/hook_manager.py +1 -0
- {runem-0.1.0 → runem-0.1.1}/runem/job_execute.py +2 -0
- {runem-0.1.0 → runem-0.1.1}/runem/runem.py +17 -40
- {runem-0.1.0 → runem-0.1.1}/runem.egg-info/PKG-INFO +1 -1
- {runem-0.1.0 → runem-0.1.1}/tests/test_job_execute.py +1 -1
- {runem-0.1.0 → runem-0.1.1}/tests/test_runem.py +9 -5
- runem-0.1.0/runem/VERSION +0 -1
- {runem-0.1.0 → runem-0.1.1}/Containerfile +0 -0
- {runem-0.1.0 → runem-0.1.1}/LICENSE +0 -0
- {runem-0.1.0 → runem-0.1.1}/MANIFEST.in +0 -0
- {runem-0.1.0 → runem-0.1.1}/README.md +0 -0
- {runem-0.1.0 → runem-0.1.1}/requirements-test.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/requirements.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/__init__.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/__main__.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/base.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/blocking_print.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/cli/initialise_options.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/cli.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/command_line.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/config.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/config_metadata.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/config_parse.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/files.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/informative_dict.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/job.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/job_filter.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/job_runner_simple_command.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/job_wrapper.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/job_wrapper_python.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/log.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/py.typed +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/report.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/run_command.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/runem_version.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/types.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem/utils.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem.egg-info/SOURCES.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem.egg-info/dependency_links.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem.egg-info/entry_points.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem.egg-info/requires.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/runem.egg-info/top_level.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/setup.cfg +0 -0
- {runem-0.1.0 → runem-0.1.1}/setup.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/__init__.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/cli/test_initialise_options.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/conftest.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/data/help_output.3.10.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/data/help_output.3.11.txt +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/intentional_test_error.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/sanitise_reports_footer.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_base.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_blocking_print.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_cli.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_config.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_config_parse.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_files.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_hook_manager.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_informative_dict.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_job.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_job_filter.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_job_runner_simple_command.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_job_wrapper.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_job_wrapper_python.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_report.py +0 -0
- {runem-0.1.0 → runem-0.1.1}/tests/test_run_command.py +0 -0
@@ -4,6 +4,27 @@ Changelog
|
|
4
4
|
|
5
5
|
(unreleased)
|
6
6
|
------------
|
7
|
+
- Merge pull request #58 from lursight/fix/completed_job_counts. [Frank
|
8
|
+
Harrison]
|
9
|
+
|
10
|
+
fix(complete-job-count): fixes and simplifies the in-progress report code
|
11
|
+
- Fix(complete-job-count): fixes and simplifies the in-progress report
|
12
|
+
code. [Frank Harrison]
|
13
|
+
|
14
|
+
The core problem was that the remaining-job-count didn't match the
|
15
|
+
number of job-labels that were being shown.
|
16
|
+
|
17
|
+
The root-cause was due to some of the job-tasks completing before the
|
18
|
+
`_update_progress()` thread had started monitoring the jobs.
|
19
|
+
|
20
|
+
We fix this by adding a new parameter to the job-executer that tracks
|
21
|
+
when job completed. It's a very simple fix and reduces the overall
|
22
|
+
complexity for about the same cost of threading-primitives.
|
23
|
+
|
24
|
+
|
25
|
+
0.1.0 (2024-11-17)
|
26
|
+
------------------
|
27
|
+
- Release: version 0.1.0 🚀 [Frank Harrison]
|
7
28
|
- Merge pull request #57 from lursight/feat/replace_halo_with_rich.
|
8
29
|
[Frank Harrison]
|
9
30
|
|
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
@@ -111,6 +111,7 @@ def job_execute_inner(
|
|
111
111
|
def job_execute(
|
112
112
|
job_config: JobConfig,
|
113
113
|
running_jobs: typing.Dict[str, str],
|
114
|
+
completed_jobs: typing.Dict[str, str],
|
114
115
|
config_metadata: ConfigMetadata,
|
115
116
|
file_lists: FilePathListLookup,
|
116
117
|
**kwargs: Unpack[HookSpecificKwargs],
|
@@ -127,5 +128,6 @@ def job_execute(
|
|
127
128
|
file_lists,
|
128
129
|
**kwargs,
|
129
130
|
)
|
131
|
+
completed_jobs[this_id] = running_jobs[this_id]
|
130
132
|
del running_jobs[this_id]
|
131
133
|
return results
|
@@ -28,7 +28,7 @@ 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
33
|
from types import TracebackType
|
34
34
|
|
@@ -41,7 +41,6 @@ from runem.config import load_project_config, load_user_configs
|
|
41
41
|
from runem.config_metadata import ConfigMetadata
|
42
42
|
from runem.config_parse import load_config_metadata
|
43
43
|
from runem.files import find_files
|
44
|
-
from runem.job import Job
|
45
44
|
from runem.job_execute import job_execute
|
46
45
|
from runem.job_filter import filter_jobs
|
47
46
|
from runem.log import error, log, warn
|
@@ -123,7 +122,7 @@ class DummySpinner(ConsoleRenderable): # pragma: no cover
|
|
123
122
|
def _update_progress(
|
124
123
|
label: str,
|
125
124
|
running_jobs: typing.Dict[str, str],
|
126
|
-
|
125
|
+
completed_jobs: typing.Dict[str, str],
|
127
126
|
all_jobs: Jobs,
|
128
127
|
is_running: ValueProxy[bool],
|
129
128
|
num_workers: int,
|
@@ -134,7 +133,6 @@ def _update_progress(
|
|
134
133
|
Args:
|
135
134
|
label (str): The identifier.
|
136
135
|
running_jobs (Dict[str, str]): The currently running jobs.
|
137
|
-
seen_jobs (List[str]): Jobs that the function has previously tracked.
|
138
136
|
all_jobs (Jobs): All jobs, encompassing both completed and running jobs.
|
139
137
|
is_running (ValueProxy[bool]): Flag indicating if jobs are still running.
|
140
138
|
num_workers (int): Indicates the number of workers performing the jobs.
|
@@ -146,47 +144,25 @@ def _update_progress(
|
|
146
144
|
else:
|
147
145
|
spinner = DummySpinner()
|
148
146
|
|
149
|
-
|
150
|
-
|
151
|
-
# The set of all job labels, and the set of completed jobs
|
152
|
-
all_job_names: typing.Set[str] = {Job.get_job_name(job) for job in all_jobs}
|
153
|
-
completed_jobs: typing.Set[str] = set()
|
154
|
-
|
155
|
-
# This dataset is used to track changes between iterations
|
156
|
-
last_running_jobs_set: typing.Set[str] = set()
|
147
|
+
last_running_jobs_set: typing.Set[str] = set()
|
157
148
|
|
149
|
+
with rich_console.status(spinner):
|
158
150
|
while is_running.value:
|
159
151
|
running_jobs_set: typing.Set[str] = set(running_jobs.values())
|
160
|
-
seen_jobs = list(running_jobs_set.union(seen_jobs)) # Update the seen jobs
|
161
|
-
|
162
|
-
# Jobs that have disappeared since last check
|
163
|
-
disappeared_jobs: typing.Set[str] = last_running_jobs_set - running_jobs_set
|
164
|
-
|
165
|
-
# Jobs that have not yet completed
|
166
|
-
remaining_jobs: typing.Set[str] = all_job_names - completed_jobs
|
167
152
|
|
168
|
-
#
|
169
|
-
workers_retiring: bool = len(remaining_jobs) <= num_workers
|
170
|
-
|
171
|
-
if workers_retiring:
|
172
|
-
# Handle edge case: a task may have disappeared whilst process was sleeping
|
173
|
-
all_completed_jobs: typing.Set[str] = all_job_names - remaining_jobs
|
174
|
-
disappeared_jobs.update(all_completed_jobs - running_jobs_set)
|
175
|
-
|
176
|
-
completed_jobs.update(disappeared_jobs)
|
177
|
-
|
178
|
-
# Prepare progress report
|
153
|
+
# Progress report
|
179
154
|
progress: str = f"{len(completed_jobs)}/{len(all_jobs)}"
|
180
|
-
running_jobs_list = printable_set(
|
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}"
|
181
159
|
if show_spinner:
|
182
|
-
spinner.text =
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
# Update the tracked dataset for the next iteration
|
187
|
-
last_running_jobs_set = running_jobs_set
|
160
|
+
spinner.text = report
|
161
|
+
else:
|
162
|
+
if last_running_jobs_set != running_jobs_set:
|
163
|
+
rich_console.log(report)
|
188
164
|
|
189
|
-
# Sleep
|
165
|
+
# Sleep for reduced CPU usage
|
190
166
|
time.sleep(0.1)
|
191
167
|
|
192
168
|
|
@@ -225,8 +201,8 @@ def _process_jobs(
|
|
225
201
|
subprocess_error: typing.Optional[BaseException] = None
|
226
202
|
|
227
203
|
with multiprocessing.Manager() as manager:
|
228
|
-
seen_jobs: ListProxy[str] = manager.list()
|
229
204
|
running_jobs: DictProxy[typing.Any, typing.Any] = manager.dict()
|
205
|
+
completed_jobs: DictProxy[typing.Any, typing.Any] = manager.dict()
|
230
206
|
is_running: ValueProxy[bool] = manager.Value("b", True)
|
231
207
|
|
232
208
|
terminal_writer_process = multiprocessing.Process(
|
@@ -234,7 +210,7 @@ def _process_jobs(
|
|
234
210
|
args=(
|
235
211
|
phase,
|
236
212
|
running_jobs,
|
237
|
-
|
213
|
+
completed_jobs,
|
238
214
|
jobs,
|
239
215
|
is_running,
|
240
216
|
num_concurrent_procs,
|
@@ -251,6 +227,7 @@ def _process_jobs(
|
|
251
227
|
zip(
|
252
228
|
jobs,
|
253
229
|
repeat(running_jobs),
|
230
|
+
repeat(completed_jobs),
|
254
231
|
repeat(config_metadata),
|
255
232
|
repeat(file_lists),
|
256
233
|
),
|
@@ -45,7 +45,7 @@ def _job_execute_and_capture_stdout(
|
|
45
45
|
ret_err: typing.Optional[BaseException] = None
|
46
46
|
with io.StringIO() as buf, redirect_stdout(buf):
|
47
47
|
try:
|
48
|
-
job_execute(job_config, running_jobs, config_metadata, file_lists)
|
48
|
+
job_execute(job_config, running_jobs, {}, config_metadata, file_lists)
|
49
49
|
except BaseException as err: # pylint: disable=broad-exception-caught
|
50
50
|
# capture the error and return it
|
51
51
|
ret_err = err
|
@@ -1675,11 +1675,12 @@ def test_progress_updater_with_running_jobs(
|
|
1675
1675
|
show_spinner: bool,
|
1676
1676
|
) -> None:
|
1677
1677
|
running_jobs: typing.Dict[str, str] = {"job1": "running", "job2": "pending"}
|
1678
|
+
completed_jobs: typing.Dict[str, str] = {}
|
1678
1679
|
with pytest.raises(SleepCalledError), multiprocessing.Manager() as manager:
|
1679
1680
|
_update_progress(
|
1680
1681
|
"dummy label",
|
1681
1682
|
running_jobs,
|
1682
|
-
|
1683
|
+
completed_jobs,
|
1683
1684
|
all_jobs=[],
|
1684
1685
|
is_running=manager.Value("b", True),
|
1685
1686
|
num_workers=1,
|
@@ -1690,6 +1691,7 @@ def test_progress_updater_with_running_jobs(
|
|
1690
1691
|
|
1691
1692
|
def test_progress_updater_with_running_jobs_and_10_jobs(mock_sleep: Mock) -> None:
|
1692
1693
|
running_jobs: typing.Dict[str, str] = {"job1": "running", "job2": "pending"}
|
1694
|
+
completed_jobs: typing.Dict[str, str] = {}
|
1693
1695
|
job_config: JobConfig = {
|
1694
1696
|
"addr": {
|
1695
1697
|
"file": __file__,
|
@@ -1715,7 +1717,7 @@ def test_progress_updater_with_running_jobs_and_10_jobs(mock_sleep: Mock) -> Non
|
|
1715
1717
|
_update_progress(
|
1716
1718
|
"dummy label",
|
1717
1719
|
running_jobs,
|
1718
|
-
|
1720
|
+
completed_jobs,
|
1719
1721
|
all_jobs=all_jobs,
|
1720
1722
|
is_running=manager.Value("b", True),
|
1721
1723
|
num_workers=1,
|
@@ -1726,11 +1728,12 @@ def test_progress_updater_with_running_jobs_and_10_jobs(mock_sleep: Mock) -> Non
|
|
1726
1728
|
|
1727
1729
|
def test_progress_updater_without_running_jobs(mock_sleep: Mock) -> None:
|
1728
1730
|
running_jobs: typing.Dict[str, str] = {}
|
1731
|
+
completed_jobs: typing.Dict[str, str] = {}
|
1729
1732
|
with pytest.raises(SleepCalledError), multiprocessing.Manager() as manager:
|
1730
1733
|
_update_progress(
|
1731
1734
|
"dummy label",
|
1732
1735
|
running_jobs,
|
1733
|
-
|
1736
|
+
completed_jobs,
|
1734
1737
|
all_jobs=[],
|
1735
1738
|
is_running=manager.Value("b", True),
|
1736
1739
|
num_workers=1,
|
@@ -1741,11 +1744,12 @@ def test_progress_updater_without_running_jobs(mock_sleep: Mock) -> None:
|
|
1741
1744
|
|
1742
1745
|
def test_progress_updater_with_empty_running_jobs(mock_sleep: Mock) -> None:
|
1743
1746
|
running_jobs: typing.Dict[str, str] = {"job1": ""}
|
1747
|
+
completed_jobs: typing.Dict[str, str] = {}
|
1744
1748
|
with pytest.raises(SleepCalledError), multiprocessing.Manager() as manager:
|
1745
1749
|
_update_progress(
|
1746
1750
|
"dummy label",
|
1747
1751
|
running_jobs,
|
1748
|
-
|
1752
|
+
completed_jobs,
|
1749
1753
|
all_jobs=[],
|
1750
1754
|
is_running=manager.Value("b", True),
|
1751
1755
|
num_workers=1,
|
@@ -1769,7 +1773,7 @@ def test_progress_updater_with_false(show_spinner: bool) -> None:
|
|
1769
1773
|
_update_progress(
|
1770
1774
|
"dummy label",
|
1771
1775
|
running_jobs,
|
1772
|
-
|
1776
|
+
{},
|
1773
1777
|
[],
|
1774
1778
|
manager.Value("b", False),
|
1775
1779
|
1,
|
runem-0.1.0/runem/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
0.1.0
|
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
|