runem 0.0.27__tar.gz → 0.0.29__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.0.27 → runem-0.0.29}/HISTORY.md +178 -0
- {runem-0.0.27 → runem-0.0.29}/PKG-INFO +8 -7
- {runem-0.0.27 → runem-0.0.29}/README.md +6 -6
- runem-0.0.29/runem/VERSION +1 -0
- {runem-0.0.27 → runem-0.0.29}/runem/cli/initialise_options.py +6 -5
- {runem-0.0.27 → runem-0.0.29}/runem/command_line.py +7 -6
- {runem-0.0.27 → runem-0.0.29}/runem/config_metadata.py +5 -4
- runem-0.0.29/runem/informative_dict.py +42 -0
- {runem-0.0.27 → runem-0.0.29}/runem/job_execute.py +55 -24
- {runem-0.0.27 → runem-0.0.29}/runem/job_filter.py +16 -3
- runem-0.0.29/runem/report.py +252 -0
- {runem-0.0.27 → runem-0.0.29}/runem/run_command.py +42 -4
- {runem-0.0.27 → runem-0.0.29}/runem/runem.py +20 -7
- {runem-0.0.27 → runem-0.0.29}/runem/types.py +21 -3
- {runem-0.0.27 → runem-0.0.29}/runem.egg-info/PKG-INFO +8 -7
- {runem-0.0.27 → runem-0.0.29}/runem.egg-info/SOURCES.txt +4 -1
- {runem-0.0.27 → runem-0.0.29}/runem.egg-info/requires.txt +1 -0
- runem-0.0.29/tests/data/help_output.3.11.txt +71 -0
- runem-0.0.29/tests/test_informative_dict.py +99 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_job_execute.py +101 -23
- {runem-0.0.27 → runem-0.0.29}/tests/test_job_filter.py +19 -9
- {runem-0.0.27 → runem-0.0.29}/tests/test_report.py +72 -29
- {runem-0.0.27 → runem-0.0.29}/tests/test_run_command.py +67 -7
- {runem-0.0.27 → runem-0.0.29}/tests/test_runem.py +45 -14
- runem-0.0.27/runem/VERSION +0 -1
- runem-0.0.27/runem/report.py +0 -145
- {runem-0.0.27 → runem-0.0.29}/Containerfile +0 -0
- {runem-0.0.27 → runem-0.0.29}/LICENSE +0 -0
- {runem-0.0.27 → runem-0.0.29}/MANIFEST.in +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/__init__.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/__main__.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/base.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/blocking_print.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/cli.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/config.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/config_parse.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/files.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/job.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/job_runner_simple_command.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/job_wrapper.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/job_wrapper_python.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/log.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/py.typed +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/runem_version.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem/utils.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem.egg-info/dependency_links.txt +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem.egg-info/entry_points.txt +0 -0
- {runem-0.0.27 → runem-0.0.29}/runem.egg-info/top_level.txt +0 -0
- {runem-0.0.27 → runem-0.0.29}/setup.cfg +0 -0
- {runem-0.0.27 → runem-0.0.29}/setup.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/__init__.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/cli/test_initialise_options.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/conftest.py +0 -0
- /runem-0.0.27/tests/data/help_output.txt → /runem-0.0.29/tests/data/help_output.3.10.txt +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/intentional_test_error.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_base.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_blocking_print.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_cli.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_config.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_config_parse.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_files.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_job.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_job_runner_simple_command.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_job_wrapper.py +0 -0
- {runem-0.0.27 → runem-0.0.29}/tests/test_job_wrapper_python.py +0 -0
@@ -4,6 +4,184 @@ Changelog
|
|
4
4
|
|
5
5
|
(unreleased)
|
6
6
|
------------
|
7
|
+
- Merge pull request #40 from lursight/fix/time_saved. [Frank Harrison]
|
8
|
+
|
9
|
+
Fix/time saved
|
10
|
+
- Feat(prettier-bars): clarifies that total is user-space time. [Frank
|
11
|
+
Harrison]
|
12
|
+
|
13
|
+
... not wall-clock or system-time
|
14
|
+
- Feat(prettier-bars): distiguishes the wall-clock bars. [Frank
|
15
|
+
Harrison]
|
16
|
+
|
17
|
+
... from the total/sum and sub-job bars, so that it's slightly easier to
|
18
|
+
see where the time is being really spent.
|
19
|
+
- Fix(time-saved): clarifies which measurement is the wall-clock time
|
20
|
+
for the entire run. [Frank Harrison]
|
21
|
+
- Fix(time-saved): add message about how long we _would_ have waited
|
22
|
+
without runem. [Frank Harrison]
|
23
|
+
- Fix(time-saved): renames all variable associated with timing reports.
|
24
|
+
[Frank Harrison]
|
25
|
+
|
26
|
+
This just makes someting which can become intractable/confusing a lot
|
27
|
+
easier to follow.
|
28
|
+
- Fix(time-saved): check that time-saved is reported correctly. [Frank
|
29
|
+
Harrison]
|
30
|
+
|
31
|
+
Here we add a test first and then fix the missing math to calculate the
|
32
|
+
time-saved by using runem. We broke this in the previous feature for
|
33
|
+
rendering the tree slightly more elegantly.
|
34
|
+
- Feat(hide-single-leafs): only show the job when it has a single child.
|
35
|
+
[Frank Harrison]
|
36
|
+
|
37
|
+
We would get duplicated information for jobs which had single
|
38
|
+
run_command invocations. This only shows sub-tasks/jobs if there are
|
39
|
+
more than one sub-tasks meaning the output looks a lot nicer & clearer.
|
40
|
+
- Chore(deps): adds setuptools as a explicit dep. [Frank Harrison]
|
41
|
+
|
42
|
+
... otherwise we get the following error (more often in python 3.12),
|
43
|
+
perhaps due to setuptools being removed from distros?:
|
44
|
+
|
45
|
+
```text
|
46
|
+
Traceback (most recent call last):
|
47
|
+
File "/var/www/mydir/virtualenvs/dev/bin/pip", line 5, in <module>
|
48
|
+
from pkg_resources import load_entry_point
|
49
|
+
ImportError: No module named pkg_resources
|
50
|
+
```
|
51
|
+
- Merge pull request #39 from lursight/feat/time_all_run_command_calls.
|
52
|
+
[Frank Harrison]
|
53
|
+
|
54
|
+
Feat/time all run command calls
|
55
|
+
- Feat(pretty-tree): refactors out the phase-job report generator.
|
56
|
+
[Frank Harrison]
|
57
|
+
|
58
|
+
This is just to make pylint happy.
|
59
|
+
- Feat(pretty-tree): makes the report tree neater. [Frank Harrison]
|
60
|
+
- Feat(time-all-sub-tasks): re-raise errors for context in ci/cd. [Frank
|
61
|
+
Harrison]
|
62
|
+
|
63
|
+
In github ci/cd we were hitting the asserts but had no context of where
|
64
|
+
they're raised from or why. This should fix that if they still occur.
|
65
|
+
- Feat(time-all-sub-tasks): adds a test to test the time-recording
|
66
|
+
functions. [Frank Harrison]
|
67
|
+
- Feat(time-all-sub-tasks): adds all run_command times to report output.
|
68
|
+
[Frank Harrison]
|
69
|
+
- Chore(type): uses a type-alias instead of manual type. [Frank
|
70
|
+
Harrison]
|
71
|
+
- Merge pull request #37 from
|
72
|
+
lursight/dependabot/github_actions/actions/upload-artifact-4. [Frank
|
73
|
+
Harrison]
|
74
|
+
|
75
|
+
chore(deps): bump actions/upload-artifact from 3 to 4
|
76
|
+
- Chore(deps): bump actions/upload-artifact from 3 to 4.
|
77
|
+
[dependabot[bot]]
|
78
|
+
|
79
|
+
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 3 to 4.
|
80
|
+
- [Release notes](https://github.com/actions/upload-artifact/releases)
|
81
|
+
- [Commits](https://github.com/actions/upload-artifact/compare/v3...v4)
|
82
|
+
|
83
|
+
---
|
84
|
+
updated-dependencies:
|
85
|
+
- dependency-name: actions/upload-artifact
|
86
|
+
dependency-type: direct:production
|
87
|
+
update-type: version-update:semver-major
|
88
|
+
...
|
89
|
+
- Merge pull request #36 from lursight/chore/more_python_versions_in_ci.
|
90
|
+
[Frank Harrison]
|
91
|
+
|
92
|
+
Chore/more python versions in ci
|
93
|
+
- Chore(github-ci): workaround for python 3.12 setuptools issue. [Frank
|
94
|
+
Harrison]
|
95
|
+
|
96
|
+
This fies what looks like an issue with pytest hooks running ci/cd (and
|
97
|
+
on local machine) where we get:
|
98
|
+
ModuleNotFoundError: No module named 'pkg_resources'
|
99
|
+
- Chore(github-ci): updates the python `--help` tests for 3.11 (and
|
100
|
+
later) [Frank Harrison]
|
101
|
+
- Chore(github-ci): test python 3.9, 3.11 and 3.12 in ci. [Frank
|
102
|
+
Harrison]
|
103
|
+
|
104
|
+
We don't bother with 3.10 because we test 3.9 and boundaries for
|
105
|
+
features are on the 3.11 version.
|
106
|
+
|
107
|
+
We don't bother with earlier than 3.9, even though 3.8 is the earliest
|
108
|
+
officially supported version.
|
109
|
+
|
110
|
+
|
111
|
+
0.0.28 (2024-03-01)
|
112
|
+
-------------------
|
113
|
+
- Release: version 0.0.28 🚀 [Frank Harrison]
|
114
|
+
- Merge pull request #35 from lursight/fix/aliases_not_setting_options.
|
115
|
+
[Frank Harrison]
|
116
|
+
|
117
|
+
Fix/aliases not setting options
|
118
|
+
- Switches the API so 'read-only' Options are the prominent type. [Frank
|
119
|
+
Harrison]
|
120
|
+
|
121
|
+
... and Writable are the recessive type, making it easier for API users to ensure they're using the correct type
|
122
|
+
- Uses OptionsReadOnly, stopping accidentally overwritting options by
|
123
|
+
individual jobs. [Frank Harrison]
|
124
|
+
- Adds a read-only version of the Options dict. [Frank Harrison]
|
125
|
+
- Ports Options type to an InformativeDict. [Frank Harrison]
|
126
|
+
|
127
|
+
This shows you what options are available in the options dict if you
|
128
|
+
look up a value that doesn't exist.
|
129
|
+
- Updates logging for clarity. [Frank Harrison]
|
130
|
+
- Merge pull request #34 from lursight/chore/github-actions. [Frank
|
131
|
+
Harrison]
|
132
|
+
|
133
|
+
Chore/GitHub actions
|
134
|
+
- Chore(github-ci): knocks out windows ci/cd for now. [Frank Harrison]
|
135
|
+
- Chore(github-ci): sets the windows ci to use utf-8. [Frank Harrison]
|
136
|
+
- Chore(github-ci): HACK ignore coverage in tested code. [Frank
|
137
|
+
Harrison]
|
138
|
+
|
139
|
+
It's not cler why the ci/cd thinks this isn't being hit, it is locally
|
140
|
+
and I can't figure out why it wouldn't be on the ci/cd, perhaps it's
|
141
|
+
down to the multithreaded pytest runs, but that would be
|
142
|
+
non-deterministic. This will probably need looking into at some point.
|
143
|
+
- Chore(ci-coverage): updates the _get_jobs_matching test case. [Frank
|
144
|
+
Harrison]
|
145
|
+
|
146
|
+
Making it more explicit and easier to follow
|
147
|
+
- Chore(ci-coverage): fixes the help-test false-positive. [Frank
|
148
|
+
Harrison]
|
149
|
+
|
150
|
+
We were always writing the help"
|
151
|
+
- Chore(ci-coverage): adds test for
|
152
|
+
test_run_command_basic_call_verbose_with_cwd. [Frank Harrison]
|
153
|
+
- Chore(ci-coverage): ignores un-hit 'communicate' mock function. [Frank
|
154
|
+
Harrison]
|
155
|
+
- Chore(github-ci): upload reports on failures. [Frank Harrison]
|
156
|
+
- Chore(github-ci): revert back to using the makefile for the redundant
|
157
|
+
checks. [Frank Harrison]
|
158
|
+
- Chore(github-ci): installs yarn deps for ci job. [Frank Harrison]
|
159
|
+
- Chore(github-ci): makes the log output less verbose by using --no-
|
160
|
+
spinner. [Frank Harrison]
|
161
|
+
- Chore(github-ci): moves the redundancy checks first to own job. [Frank
|
162
|
+
Harrison]
|
163
|
+
- Chore(github-ci): runs two basic checks for redunancy during ci.
|
164
|
+
[Frank Harrison]
|
165
|
+
|
166
|
+
This should help catch errors where runem has been broken and returns a
|
167
|
+
false-positive when run against itself.
|
168
|
+
- Chore(github-ci): runs runem against itself. [Frank Harrison]
|
169
|
+
|
170
|
+
This could have drawbacks later so we will think about adding some
|
171
|
+
redundancy to the ci checks.
|
172
|
+
- Merge pull request #33 from lursight/fix/mutiline_stdout. [Frank
|
173
|
+
Harrison]
|
174
|
+
|
175
|
+
fix(stdout-parsing): ensures that trailing newlines are handled
|
176
|
+
- Fix(stdout-parsing): ensures that trailing newlines are handled.
|
177
|
+
[Frank Harrison]
|
178
|
+
|
179
|
+
There will be a slight performance cost to this, hopefully not too much.
|
180
|
+
|
181
|
+
|
182
|
+
0.0.27 (2024-02-26)
|
183
|
+
-------------------
|
184
|
+
- Release: version 0.0.27 🚀 [Frank Harrison]
|
7
185
|
- Merge pull request #32 from lursight/feat/min-version_check. [Frank
|
8
186
|
Harrison]
|
9
187
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: runem
|
3
|
-
Version: 0.0.
|
3
|
+
Version: 0.0.29
|
4
4
|
Summary: Awesome runem created by lursight
|
5
5
|
Home-page: https://github.com/lursight/runem/
|
6
6
|
Author: lursight
|
@@ -26,6 +26,7 @@ Requires-Dist: pytest-cov==4.1.0; extra == "test"
|
|
26
26
|
Requires-Dist: pytest-profiling==1.7.0; extra == "test"
|
27
27
|
Requires-Dist: pytest-xdist==3.3.1; extra == "test"
|
28
28
|
Requires-Dist: pytest==7.4.3; extra == "test"
|
29
|
+
Requires-Dist: setuptools; extra == "test"
|
29
30
|
Requires-Dist: termplotlib==0.3.9; extra == "test"
|
30
31
|
Requires-Dist: types-PyYAML==6.0.12.12; extra == "test"
|
31
32
|
Requires-Dist: requests-mock==1.10.0; extra == "test"
|
@@ -400,12 +401,12 @@ runem: reports:
|
|
400
401
|
runem: runem: 8.820488s
|
401
402
|
runem: ├runem.pre-build: 0.019031s
|
402
403
|
runem: ├runem.run-phases: 8.801317s
|
403
|
-
runem: ├pre-run (
|
404
|
+
runem: ├pre-run (user-time): 0.00498s
|
404
405
|
runem: │├pre-run.install python requirements: 2.6e-05s
|
405
406
|
runem: │├pre-run.ls -alh runem: 0.004954s
|
406
|
-
runem: ├edit (
|
407
|
+
runem: ├edit (user-time): 0.557559s
|
407
408
|
runem: │├edit.reformat py: 0.557559s
|
408
|
-
runem: ├analysis (
|
409
|
+
runem: ├analysis (user-time): 21.526145s
|
409
410
|
runem: │├analysis.pylint py: 7.457029s
|
410
411
|
runem: │├analysis.flake8 py: 0.693754s
|
411
412
|
runem: │├analysis.mypy py: 1.071956s
|
@@ -430,12 +431,12 @@ runem: reports:
|
|
430
431
|
runem [14.174612] ███████████████▋
|
431
432
|
├runem.pre-build [ 0.025858]
|
432
433
|
├runem.run-phases [14.148587] ███████████████▋
|
433
|
-
├pre-run (
|
434
|
+
├pre-run (user-time) [ 0.005825]
|
434
435
|
│├pre-run.install python requirements [ 0.000028]
|
435
436
|
│├pre-run.ls -alh runem [ 0.005797]
|
436
|
-
├edit (
|
437
|
+
├edit (user-time) [ 0.579153] ▋
|
437
438
|
│├edit.reformat py [ 0.579153] ▋
|
438
|
-
├analysis (
|
439
|
+
├analysis (user-time) [36.231034] ████████████████████████████████████████
|
439
440
|
│├analysis.pylint py [12.738303] ██████████████▏
|
440
441
|
│├analysis.flake8 py [ 0.798575] ▉
|
441
442
|
│├analysis.mypy py [ 0.335984] ▍
|
@@ -367,12 +367,12 @@ runem: reports:
|
|
367
367
|
runem: runem: 8.820488s
|
368
368
|
runem: ├runem.pre-build: 0.019031s
|
369
369
|
runem: ├runem.run-phases: 8.801317s
|
370
|
-
runem: ├pre-run (
|
370
|
+
runem: ├pre-run (user-time): 0.00498s
|
371
371
|
runem: │├pre-run.install python requirements: 2.6e-05s
|
372
372
|
runem: │├pre-run.ls -alh runem: 0.004954s
|
373
|
-
runem: ├edit (
|
373
|
+
runem: ├edit (user-time): 0.557559s
|
374
374
|
runem: │├edit.reformat py: 0.557559s
|
375
|
-
runem: ├analysis (
|
375
|
+
runem: ├analysis (user-time): 21.526145s
|
376
376
|
runem: │├analysis.pylint py: 7.457029s
|
377
377
|
runem: │├analysis.flake8 py: 0.693754s
|
378
378
|
runem: │├analysis.mypy py: 1.071956s
|
@@ -397,12 +397,12 @@ runem: reports:
|
|
397
397
|
runem [14.174612] ███████████████▋
|
398
398
|
├runem.pre-build [ 0.025858]
|
399
399
|
├runem.run-phases [14.148587] ███████████████▋
|
400
|
-
├pre-run (
|
400
|
+
├pre-run (user-time) [ 0.005825]
|
401
401
|
│├pre-run.install python requirements [ 0.000028]
|
402
402
|
│├pre-run.ls -alh runem [ 0.005797]
|
403
|
-
├edit (
|
403
|
+
├edit (user-time) [ 0.579153] ▋
|
404
404
|
│├edit.reformat py [ 0.579153] ▋
|
405
|
-
├analysis (
|
405
|
+
├analysis (user-time) [36.231034] ████████████████████████████████████████
|
406
406
|
│├analysis.pylint py [12.738303] ██████████████▏
|
407
407
|
│├analysis.flake8 py [ 0.798575] ▉
|
408
408
|
│├analysis.mypy py [ 0.335984] ▍
|
@@ -0,0 +1 @@
|
|
1
|
+
0.0.29
|
@@ -1,21 +1,22 @@
|
|
1
1
|
import argparse
|
2
2
|
|
3
3
|
from runem.config_metadata import ConfigMetadata
|
4
|
-
from runem.
|
4
|
+
from runem.informative_dict import InformativeDict
|
5
|
+
from runem.types import OptionsWritable
|
5
6
|
|
6
7
|
|
7
8
|
def initialise_options(
|
8
9
|
config_metadata: ConfigMetadata,
|
9
10
|
args: argparse.Namespace,
|
10
|
-
) ->
|
11
|
+
) -> OptionsWritable:
|
11
12
|
"""Initialises and returns the set of options to use for this run.
|
12
13
|
|
13
14
|
Returns the options dictionary
|
14
15
|
"""
|
15
16
|
|
16
|
-
options:
|
17
|
-
option["name"]: option["default"] for option in config_metadata.options_config
|
18
|
-
|
17
|
+
options: OptionsWritable = InformativeDict(
|
18
|
+
{option["name"]: option["default"] for option in config_metadata.options_config}
|
19
|
+
)
|
19
20
|
if config_metadata.options_config and args.overrides_on: # pragma: no branch
|
20
21
|
for option_name in args.overrides_on: # pragma: no branch
|
21
22
|
options[option_name] = True
|
@@ -5,9 +5,10 @@ import sys
|
|
5
5
|
import typing
|
6
6
|
|
7
7
|
from runem.config_metadata import ConfigMetadata
|
8
|
+
from runem.informative_dict import InformativeDict
|
8
9
|
from runem.log import log
|
9
10
|
from runem.runem_version import get_runem_version
|
10
|
-
from runem.types import JobNames, OptionConfig,
|
11
|
+
from runem.types import JobNames, OptionConfig, OptionsWritable
|
11
12
|
from runem.utils import printable_set
|
12
13
|
|
13
14
|
|
@@ -175,7 +176,7 @@ def parse_args(
|
|
175
176
|
# cleanly exit
|
176
177
|
sys.exit(0)
|
177
178
|
|
178
|
-
options:
|
179
|
+
options: OptionsWritable = initialise_options(config_metadata, args)
|
179
180
|
|
180
181
|
if not _validate_filters(config_metadata, args):
|
181
182
|
sys.exit(1)
|
@@ -245,15 +246,15 @@ def _validate_filters(
|
|
245
246
|
def initialise_options(
|
246
247
|
config_metadata: ConfigMetadata,
|
247
248
|
args: argparse.Namespace,
|
248
|
-
) ->
|
249
|
+
) -> OptionsWritable:
|
249
250
|
"""Initialises and returns the set of options to use for this run.
|
250
251
|
|
251
252
|
Returns the options dictionary
|
252
253
|
"""
|
253
254
|
|
254
|
-
options:
|
255
|
-
option["name"]: option["default"] for option in config_metadata.options_config
|
256
|
-
|
255
|
+
options: OptionsWritable = InformativeDict(
|
256
|
+
{option["name"]: option["default"] for option in config_metadata.options_config}
|
257
|
+
)
|
257
258
|
if config_metadata.options_config and args.overrides_on: # pragma: no branch
|
258
259
|
for option_name in args.overrides_on: # pragma: no branch
|
259
260
|
options[option_name] = True
|
@@ -1,12 +1,13 @@
|
|
1
1
|
import argparse
|
2
2
|
import pathlib
|
3
3
|
|
4
|
+
from runem.informative_dict import InformativeDict
|
4
5
|
from runem.types import (
|
5
6
|
JobNames,
|
6
7
|
JobPhases,
|
7
8
|
JobTags,
|
8
9
|
OptionConfigs,
|
9
|
-
|
10
|
+
OptionsWritable,
|
10
11
|
OrderedPhases,
|
11
12
|
PhaseGroupedJobs,
|
12
13
|
TagFileFilters,
|
@@ -24,7 +25,7 @@ class ConfigMetadata:
|
|
24
25
|
all_job_phases: JobPhases # the set of job-phases (should be subset of 'phases')
|
25
26
|
all_job_tags: JobTags # the set of job-tags (used for filtering)
|
26
27
|
|
27
|
-
options:
|
28
|
+
options: OptionsWritable # the final configured options to pass to jobs
|
28
29
|
|
29
30
|
args: argparse.Namespace # the raw cli args, probably missing information
|
30
31
|
jobs_to_run: JobNames # superset of job-name candidates to run, from cli+config
|
@@ -52,7 +53,7 @@ class ConfigMetadata:
|
|
52
53
|
self.all_job_phases = all_job_phases
|
53
54
|
self.all_job_tags = all_job_tags
|
54
55
|
|
55
|
-
self.options =
|
56
|
+
self.options = InformativeDict() # shows useful errors on bad-option lookups
|
56
57
|
|
57
58
|
self.args = (
|
58
59
|
argparse.Namespace()
|
@@ -69,7 +70,7 @@ class ConfigMetadata:
|
|
69
70
|
phases_to_run: JobPhases,
|
70
71
|
tags_to_run: JobTags,
|
71
72
|
tags_to_avoid: JobTags,
|
72
|
-
options:
|
73
|
+
options: OptionsWritable,
|
73
74
|
) -> None:
|
74
75
|
self.options = options
|
75
76
|
self.args = args
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import typing
|
2
|
+
|
3
|
+
# Define type variables for key and value to be used in the custom dictionary
|
4
|
+
K = typing.TypeVar("K")
|
5
|
+
V = typing.TypeVar("V")
|
6
|
+
|
7
|
+
|
8
|
+
class InformativeDict(typing.Dict[K, V], typing.Generic[K, V]):
|
9
|
+
"""A dictionary type that prints out the available keys."""
|
10
|
+
|
11
|
+
def __getitem__(self, key: K) -> V:
|
12
|
+
"""Attempt to retrieve an item, raising a detailed exception if the key is not
|
13
|
+
found."""
|
14
|
+
try:
|
15
|
+
return super().__getitem__(key)
|
16
|
+
except KeyError:
|
17
|
+
available_keys: typing.Iterable[str] = (str(k) for k in self.keys())
|
18
|
+
raise KeyError(
|
19
|
+
f"Key '{key}' not found. Available keys: {', '.join(available_keys)}"
|
20
|
+
) from None
|
21
|
+
|
22
|
+
|
23
|
+
class ReadOnlyInformativeDict(InformativeDict[K, V], typing.Generic[K, V]):
|
24
|
+
"""A read-only variant of the above."""
|
25
|
+
|
26
|
+
def __setitem__(self, key: K, value: V) -> None:
|
27
|
+
raise NotImplementedError("This dictionary is read-only")
|
28
|
+
|
29
|
+
def __delitem__(self, key: K) -> None:
|
30
|
+
raise NotImplementedError("This dictionary is read-only")
|
31
|
+
|
32
|
+
def pop(self, *args: typing.Any, **kwargs: typing.Any) -> V:
|
33
|
+
raise NotImplementedError("This dictionary is read-only")
|
34
|
+
|
35
|
+
def popitem(self) -> typing.Tuple[K, V]:
|
36
|
+
raise NotImplementedError("This dictionary is read-only")
|
37
|
+
|
38
|
+
def clear(self) -> None:
|
39
|
+
raise NotImplementedError("This dictionary is read-only")
|
40
|
+
|
41
|
+
def update(self, *args: typing.Any, **kwargs: typing.Any) -> None:
|
42
|
+
raise NotImplementedError("This dictionary is read-only")
|
@@ -7,24 +7,34 @@ from datetime import timedelta
|
|
7
7
|
from timeit import default_timer as timer
|
8
8
|
|
9
9
|
from runem.config_metadata import ConfigMetadata
|
10
|
+
from runem.informative_dict import ReadOnlyInformativeDict
|
10
11
|
from runem.job import Job
|
11
12
|
from runem.job_wrapper import get_job_wrapper
|
12
13
|
from runem.log import log
|
13
|
-
from runem.types import
|
14
|
+
from runem.types import (
|
15
|
+
FilePathListLookup,
|
16
|
+
JobConfig,
|
17
|
+
JobFunction,
|
18
|
+
JobReturn,
|
19
|
+
JobTags,
|
20
|
+
JobTiming,
|
21
|
+
TimingEntries,
|
22
|
+
TimingEntry,
|
23
|
+
)
|
14
24
|
|
15
25
|
|
16
26
|
def job_execute_inner(
|
17
27
|
job_config: JobConfig,
|
18
28
|
config_metadata: ConfigMetadata,
|
19
29
|
file_lists: FilePathListLookup,
|
20
|
-
) -> typing.Tuple[
|
30
|
+
) -> typing.Tuple[JobTiming, JobReturn]:
|
21
31
|
"""Wrapper for running a job inside a sub-process.
|
22
32
|
|
23
33
|
Returns the time information and any reports the job generated
|
24
34
|
"""
|
25
35
|
label = Job.get_job_name(job_config)
|
26
36
|
if config_metadata.args.verbose:
|
27
|
-
log(f"START: {label}")
|
37
|
+
log(f"START: '{label}'")
|
28
38
|
root_path: pathlib.Path = config_metadata.cfg_filepath.parent
|
29
39
|
function: JobFunction
|
30
40
|
job_tags: typing.Optional[JobTags] = Job.get_job_tags(job_config)
|
@@ -37,7 +47,19 @@ def job_execute_inner(
|
|
37
47
|
if not file_list:
|
38
48
|
# no files to work on
|
39
49
|
log(f"WARNING: skipping job '{label}', no files for job")
|
40
|
-
return
|
50
|
+
return {
|
51
|
+
"job": (f"{label}: no files!", timedelta(0)),
|
52
|
+
"commands": [],
|
53
|
+
}, None
|
54
|
+
|
55
|
+
sub_command_timings: TimingEntries = []
|
56
|
+
|
57
|
+
def _record_sub_job_time(label: str, timing: timedelta) -> None:
|
58
|
+
"""Record timing information for sub-commands/tasks, atomically.
|
59
|
+
|
60
|
+
For example inside of run_command() calls
|
61
|
+
"""
|
62
|
+
sub_command_timings.append((label, timing))
|
41
63
|
|
42
64
|
if (
|
43
65
|
"ctx" in job_config
|
@@ -53,29 +75,38 @@ def job_execute_inner(
|
|
53
75
|
start = timer()
|
54
76
|
func_signature = inspect.signature(function)
|
55
77
|
if config_metadata.args.verbose:
|
56
|
-
log(f"job: running {Job.get_job_name(job_config)}")
|
78
|
+
log(f"job: running: '{Job.get_job_name(job_config)}'")
|
57
79
|
reports: JobReturn
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
80
|
+
try:
|
81
|
+
if "args" in func_signature.parameters:
|
82
|
+
reports = function( # type: ignore # FIXME: which function do we have?
|
83
|
+
config_metadata.args, config_metadata.options, file_list
|
84
|
+
)
|
85
|
+
else:
|
86
|
+
reports = function(
|
87
|
+
options=ReadOnlyInformativeDict(config_metadata.options), # type: ignore
|
88
|
+
file_list=file_list,
|
89
|
+
procs=config_metadata.args.procs,
|
90
|
+
root_path=root_path,
|
91
|
+
verbose=config_metadata.args.verbose,
|
92
|
+
# unpack useful data points from the job_config
|
93
|
+
label=Job.get_job_name(job_config),
|
94
|
+
job=job_config,
|
95
|
+
record_sub_job_time=_record_sub_job_time,
|
96
|
+
)
|
97
|
+
except BaseException: # pylint: disable=broad-exception-caught
|
98
|
+
# log that we hit an error on this job and re-raise
|
99
|
+
log(decorate=False)
|
100
|
+
log(f"job: ERROR: job '{Job.get_job_name(job_config)}' failed to complete!")
|
101
|
+
# re-raise
|
102
|
+
raise
|
103
|
+
|
73
104
|
end = timer()
|
74
105
|
time_taken: timedelta = timedelta(seconds=end - start)
|
75
106
|
if config_metadata.args.verbose:
|
76
|
-
log(f"DONE: {label}: {time_taken}")
|
77
|
-
|
78
|
-
return (
|
107
|
+
log(f"job: DONE: '{label}': {time_taken}")
|
108
|
+
this_job_timing_data: TimingEntry = (label, time_taken)
|
109
|
+
return ({"job": this_job_timing_data, "commands": sub_command_timings}, reports)
|
79
110
|
|
80
111
|
|
81
112
|
def job_execute(
|
@@ -83,7 +114,7 @@ def job_execute(
|
|
83
114
|
running_jobs: typing.Dict[str, str],
|
84
115
|
config_metadata: ConfigMetadata,
|
85
116
|
file_lists: FilePathListLookup,
|
86
|
-
) -> typing.Tuple[
|
117
|
+
) -> typing.Tuple[JobTiming, JobReturn]:
|
87
118
|
"""Thin-wrapper around job_execute_inner needed for mocking in tests.
|
88
119
|
|
89
120
|
Needed for faster tests.
|
@@ -65,6 +65,10 @@ def _get_jobs_matching(
|
|
65
65
|
filtered_jobs: PhaseGroupedJobs,
|
66
66
|
verbose: bool,
|
67
67
|
) -> None:
|
68
|
+
"""Via filtered_jobs, filters 'jobs' that match the given phase and and tags.
|
69
|
+
|
70
|
+
Warns if the job-name isn't found in list of valid job-names.
|
71
|
+
"""
|
68
72
|
phase_jobs: typing.List[JobConfig] = jobs[phase]
|
69
73
|
|
70
74
|
job: JobConfig
|
@@ -74,7 +78,10 @@ def _get_jobs_matching(
|
|
74
78
|
|
75
79
|
job_name: str = Job.get_job_name(job)
|
76
80
|
if job_name not in job_names:
|
77
|
-
|
81
|
+
# test test_get_jobs_matching_when_job_not_in_valid_job_names should
|
82
|
+
# cover the follow in Ci but does not for some reason I don't have
|
83
|
+
# time to look in to. /FH
|
84
|
+
if verbose: # pragma: FIXME: add code coverage
|
78
85
|
log(
|
79
86
|
(
|
80
87
|
f"not running job '{job_name}' because it isn't in the "
|
@@ -118,7 +125,10 @@ def filter_jobs( # noqa: C901
|
|
118
125
|
filtered_jobs: PhaseGroupedJobs = defaultdict(list)
|
119
126
|
for phase in config_metadata.phases:
|
120
127
|
if phase not in phases_to_run:
|
121
|
-
|
128
|
+
# test test_get_jobs_matching_when_job_not_in_valid_job_names should
|
129
|
+
# cover the follow in Ci but does not for some reason I don't have
|
130
|
+
# time to look in to. /FH
|
131
|
+
if verbose: # pragma: FIXME: add code coverage
|
122
132
|
log(f"skipping phase '{phase}'")
|
123
133
|
continue
|
124
134
|
_get_jobs_matching(
|
@@ -131,7 +141,10 @@ def filter_jobs( # noqa: C901
|
|
131
141
|
verbose=verbose,
|
132
142
|
)
|
133
143
|
if len(filtered_jobs[phase]) == 0:
|
134
|
-
|
144
|
+
# test test_get_jobs_matching_when_job_not_in_valid_job_names should
|
145
|
+
# cover the follow in Ci but does not for some reason I don't have
|
146
|
+
# time to look in to. /FH
|
147
|
+
if verbose: # pragma: FIXME: add code coverage
|
135
148
|
log(f"No jobs for phase '{phase}' tags {printable_set(tags_to_run)}")
|
136
149
|
continue
|
137
150
|
|