lsst-ctrl-bps 29.1.0rc4__tar.gz → 30.0.0rc3__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.
- {lsst_ctrl_bps-29.1.0rc4/python/lsst_ctrl_bps.egg-info → lsst_ctrl_bps-30.0.0rc3}/PKG-INFO +3 -3
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/doc/lsst.ctrl.bps/CHANGES.rst +56 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/doc/lsst.ctrl.bps/quickstart.rst +126 -26
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/pyproject.toml +3 -3
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/bps_config.py +61 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/bps_reports.py +131 -43
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/bps_utils.py +35 -1
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cli/cmd/__init__.py +14 -1
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cli/cmd/commands.py +19 -1
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/clustered_quantum_graph.py +48 -61
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/construct.py +108 -5
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/drivers.py +76 -17
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/etc/bps_defaults.yaml +9 -3
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/generic_workflow.py +12 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/initialize.py +3 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/pre_transform.py +18 -8
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/quantum_clustering_funcs.py +96 -83
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/report.py +46 -25
- lsst_ctrl_bps-30.0.0rc3/python/lsst/ctrl/bps/status.py +101 -0
- lsst_ctrl_bps-30.0.0rc3/python/lsst/ctrl/bps/tests/config_test_utils.py +114 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/transform.py +7 -25
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/version.py +1 -1
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/wms_service.py +68 -30
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3/python/lsst_ctrl_bps.egg-info}/PKG-INFO +3 -3
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst_ctrl_bps.egg-info/SOURCES.txt +5 -0
- lsst_ctrl_bps-29.1.0rc4/tests/test_report.py → lsst_ctrl_bps-30.0.0rc3/tests/test_bps_reports.py +118 -39
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_bps_utils.py +12 -1
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_bpsconfig.py +133 -1
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_cli_commands.py +67 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_clustered_quantum_graph.py +18 -22
- lsst_ctrl_bps-30.0.0rc3/tests/test_construct.py +371 -0
- lsst_ctrl_bps-30.0.0rc3/tests/test_drivers.py +329 -0
- lsst_ctrl_bps-30.0.0rc3/tests/test_initialize.py +180 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_pre_transform.py +8 -7
- lsst_ctrl_bps-30.0.0rc3/tests/test_report.py +72 -0
- lsst_ctrl_bps-30.0.0rc3/tests/test_status.py +50 -0
- lsst_ctrl_bps-29.1.0rc4/tests/test_construct.py +0 -122
- lsst_ctrl_bps-29.1.0rc4/tests/test_drivers.py +0 -121
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/COPYRIGHT +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/LICENSE +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/MANIFEST.in +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/README.md +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/bsd_license.txt +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/doc/lsst.ctrl.bps/index.rst +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/gpl-v3.0.txt +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/__init__.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/__init__.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/__init__.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/_exceptions.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/bps_draw.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cancel.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cli/__init__.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cli/bps.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cli/opt/__init__.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cli/opt/arguments.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cli/opt/option_groups.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/cli/opt/options.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/constants.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/ping.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/prepare.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/restart.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/submit.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst/ctrl/bps/tests/gw_test_utils.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst_ctrl_bps.egg-info/dependency_links.txt +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst_ctrl_bps.egg-info/entry_points.txt +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst_ctrl_bps.egg-info/requires.txt +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst_ctrl_bps.egg-info/top_level.txt +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/python/lsst_ctrl_bps.egg-info/zip-safe +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/setup.cfg +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_generic_workflow.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_ping.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_quantum_clustering_funcs.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_transform.py +0 -0
- {lsst_ctrl_bps-29.1.0rc4 → lsst_ctrl_bps-30.0.0rc3}/tests/test_wms_service.py +0 -0
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lsst-ctrl-bps
|
|
3
|
-
Version:
|
|
3
|
+
Version: 30.0.0rc3
|
|
4
4
|
Summary: Pluggable execution of workflow graphs from Rubin pipelines.
|
|
5
5
|
Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
|
|
6
|
-
License: BSD
|
|
6
|
+
License-Expression: BSD-3-Clause OR GPL-3.0-or-later
|
|
7
7
|
Project-URL: Homepage, https://github.com/lsst/ctrl_bps
|
|
8
8
|
Keywords: lsst
|
|
9
9
|
Classifier: Intended Audience :: Science/Research
|
|
10
|
-
Classifier: License :: OSI Approved :: BSD License
|
|
11
10
|
Classifier: Operating System :: OS Independent
|
|
12
11
|
Classifier: Programming Language :: Python :: 3
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
16
|
Classifier: Topic :: Scientific/Engineering :: Astronomy
|
|
17
17
|
Requires-Python: >=3.11.0
|
|
18
18
|
Description-Content-Type: text/markdown
|
|
@@ -1,3 +1,59 @@
|
|
|
1
|
+
lsst-ctrl-bps v30.0.0 (2026-01-16)
|
|
2
|
+
==================================
|
|
3
|
+
|
|
4
|
+
New Features
|
|
5
|
+
------------
|
|
6
|
+
|
|
7
|
+
- Added support for transferring input files to the execution site and bringing the produced output files back to the submit site when using ``bps submitcmd``. (`DM-48479 <https://rubinobs.atlassian.net/browse/DM-48479>`_)
|
|
8
|
+
- Added ``bpsGenerateConfig`` and ``bpsEval`` to run functions to produce config values. ``bpsGenerateConfig`` is used when inserting or updating one or more key/value pairs. ``bpsEval`` is for replacing part of a string value. (`DM-50616 <https://rubinobs.atlassian.net/browse/DM-50616>`_)
|
|
9
|
+
|
|
10
|
+
Bug Fixes
|
|
11
|
+
---------
|
|
12
|
+
|
|
13
|
+
- Fixed bug where the ``return_exit_codes`` command line value was not passed to the plugin's report function. While it was correctly used when displaying the report, not having the value didn't allow the plugin to optimize its report function. (`DM-52791 <https://rubinobs.atlassian.net/browse/DM-52791>`_)
|
|
14
|
+
|
|
15
|
+
Performance Enhancement
|
|
16
|
+
-----------------------
|
|
17
|
+
|
|
18
|
+
- Switched the default ``finalJob`` implementation to the new ``aggregate-graph`` command, which makes use of multiple cores much more effectively than ``transfer-from-graph``. (`DM-52360 <https://rubinobs.atlassian.net/browse/DM-52360>`_)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Other Changes and Additions
|
|
22
|
+
---------------------------
|
|
23
|
+
|
|
24
|
+
- Removed exit code 1 from the default for ``finalJob``\ 's ``retryUnlessExit`` as the majority of these are related to system issues which could be transient. (`DM-51313 <https://rubinobs.atlassian.net/browse/DM-51313>`_)
|
|
25
|
+
- Modified the BPS report driver so it compiles exit code summary only when necessary, i.e., when ``--return-exit-codes`` option was used with ``bps report``. (`DM-52898 <https://rubinobs.atlassian.net/browse/DM-52898>`_)
|
|
26
|
+
- Added a custom __new__ method to the GenericWorkflow class so **ctrl_bps** can work when using NetworkX 3.6. (`DM-53492 <https://rubinobs.atlassian.net/browse/DM-53492>`_)
|
|
27
|
+
- Made the BPS reporting mechanism a bit more robust. If the BPS plugin does not explicitly includes jobs labels for which there are no failures in the run's exit code summary, it will try to use the run's job summary to do that. (`DM-51261 <https://rubinobs.atlassian.net/browse/DM-51261>`_)
|
|
28
|
+
- Used the new ``PredictedQuantumGraph`` class internally and default to the new QG file format via the "``.qg``" extension. (`DM-52339 <https://rubinobs.atlassian.net/browse/DM-52339>`_)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
lsst-ctrl-bps v29.1.0 (2025-06-13)
|
|
32
|
+
==================================
|
|
33
|
+
|
|
34
|
+
New Features
|
|
35
|
+
------------
|
|
36
|
+
|
|
37
|
+
- Added ability to enforce workflow job ordering beyond data dependencies. (`DM-46294 <https://rubinobs.atlassian.net/browse/DM-46294>`_)
|
|
38
|
+
- Added partitioning of clusters for when clustering on two dimensions makes too many short jobs but one dimension makes too few long jobs. (`DM-49240 <https://rubinobs.atlassian.net/browse/DM-49240>`_)
|
|
39
|
+
- Added status subcommand to quickly print overall run status. (`DM-50619 <https://rubinobs.atlassian.net/browse/DM-50619>`_)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
API Changes
|
|
43
|
+
-----------
|
|
44
|
+
|
|
45
|
+
- The ``check_clustering_config`` function is now public to support testing of clustering configuration YAML files. (`DM-49222 <https://rubinobs.atlassian.net/browse/DM-49222>`_)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
Other Changes and Additions
|
|
49
|
+
---------------------------
|
|
50
|
+
|
|
51
|
+
- BPS commands involved in making submissions now exit with the exit codes of the subprocess that BPS spawns to generate or update the run's quantum graph if any failure occurs. (`DM-49744 <https://rubinobs.atlassian.net/browse/DM-49744>`_)
|
|
52
|
+
- Added check that every job label in a group config has at least one job. (`DM-49747 <https://rubinobs.atlassian.net/browse/DM-49747>`_)
|
|
53
|
+
- Ensured all job environment values are strings in the ``GenericWorkflow``. (`DM-50498 <https://rubinobs.atlassian.net/browse/DM-50498>`_)
|
|
54
|
+
- Added logging instructions to documentation. (`DM-50243 <https://rubinobs.atlassian.net/browse/DM-50243>`_)
|
|
55
|
+
- Decreased buffering for writing ``stdout``/``stderr`` to file when generating or updating ``QuantumGraph``. (`DM-50294 <https://rubinobs.atlassian.net/browse/DM-50294>`_)
|
|
56
|
+
|
|
1
57
|
lsst-ctrl-bps v29.0.0 (2025-03-25)
|
|
2
58
|
==================================
|
|
3
59
|
|
|
@@ -157,7 +157,7 @@ or a pre-made file containing a serialized QuantumGraph, for example
|
|
|
157
157
|
|
|
158
158
|
.. code-block:: YAML
|
|
159
159
|
|
|
160
|
-
qgraphFile: pipelines_check_w_2020_45.
|
|
160
|
+
qgraphFile: pipelines_check_w_2020_45.qg
|
|
161
161
|
|
|
162
162
|
.. warning::
|
|
163
163
|
|
|
@@ -265,7 +265,7 @@ arguments, e.g.:
|
|
|
265
265
|
.. code-block:: yaml
|
|
266
266
|
|
|
267
267
|
customJob:
|
|
268
|
-
executable: "${HOME}/scripts/
|
|
268
|
+
executable: "${HOME}/scripts/do_stuff.sh"
|
|
269
269
|
arguments: "2"
|
|
270
270
|
|
|
271
271
|
# Uncomment settings below to disable automatic memory scaling and retries
|
|
@@ -276,16 +276,62 @@ arguments, e.g.:
|
|
|
276
276
|
|
|
277
277
|
where ``executable`` specifies the path to the executable to run and
|
|
278
278
|
``arguments`` is a list of arguments to be supplied to the executable as part
|
|
279
|
-
of the command line.
|
|
279
|
+
of the command line. If your executable does not take any command line
|
|
280
|
+
arguments set ``arguments`` to an empty string.
|
|
280
281
|
|
|
281
282
|
.. note::
|
|
282
283
|
|
|
283
|
-
|
|
284
|
-
|
|
284
|
+
The script specified by ``customJob.executable`` is copied to the run's
|
|
285
|
+
submit directory and this copy (not the original script) is being submitted
|
|
286
|
+
for execution. As a result, making any changes to the original script after
|
|
287
|
+
the run has been submitted will have no effect even if the run is still
|
|
288
|
+
in the WMS work queue waiting for execution.
|
|
289
|
+
|
|
290
|
+
If the script requires any input files that should be transferred to the
|
|
291
|
+
execution site as well and/or produces output files that should be brought back
|
|
292
|
+
specify them as follows:
|
|
293
|
+
|
|
294
|
+
.. code-block:: yaml
|
|
295
|
+
|
|
296
|
+
customJob:
|
|
297
|
+
executable: "${HOME}/scripts/do_stuff.sh"
|
|
298
|
+
arguments: "-o {outfile} {infile}"
|
|
299
|
+
inputs:
|
|
300
|
+
infile: path/to/input/file
|
|
301
|
+
outputs:
|
|
302
|
+
outfile: path/to/output/file
|
|
285
303
|
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
304
|
+
# Uncomment settings below to disable automatic memory scaling and retries
|
|
305
|
+
# which BPS enables by default.
|
|
306
|
+
#
|
|
307
|
+
# memoryMultiplier: 1
|
|
308
|
+
# numberOfRetries: 1
|
|
309
|
+
|
|
310
|
+
The paths in ``inputs`` specify files as they are accessed on the submit site.
|
|
311
|
+
They can be absolute or relative to the current working directory as the run is
|
|
312
|
+
submitted. The input files will be copied to the run's submit directory.
|
|
313
|
+
These copies (not the original files) will be submitted along with the script's
|
|
314
|
+
copy for execution. During the execution BPS will transfer these copies into a
|
|
315
|
+
single flat directory -- job's scratch directory on the execute machine.
|
|
316
|
+
|
|
317
|
+
The paths in ``outputs`` specifies the paths on the submit site the output
|
|
318
|
+
files will be copied to after job's completion. As input paths they can be
|
|
319
|
+
either absolute or relative. However, on the execution site, the script is
|
|
320
|
+
expected to write all its output files directly to job's scratch directory
|
|
321
|
+
(assumed to be the current working directory when the job starts unless stated
|
|
322
|
+
otherwise in the WMS-specific documentation).
|
|
323
|
+
|
|
324
|
+
As a result, both input and output files base names *must* be unique.
|
|
325
|
+
|
|
326
|
+
.. note::
|
|
327
|
+
|
|
328
|
+
Currently, BPS itself doesn't verify if the file declared in ``outputs`` was
|
|
329
|
+
produced by the script. Whether a missing output file will be considered an
|
|
330
|
+
error depends entirely on WMS in use.
|
|
331
|
+
|
|
332
|
+
The config files shown above will instruct BPS to create a special single-job
|
|
333
|
+
*workflow* to run your script. That workflow will be submitted for execution
|
|
334
|
+
as any other workflow.
|
|
289
335
|
|
|
290
336
|
As a result, the submission process for a custom script looks quite similar
|
|
291
337
|
to the submission process of regular payload jobs (i.e. jobs running
|
|
@@ -305,15 +351,6 @@ There are few things you need to keep in mind though:
|
|
|
305
351
|
instructions exist in the submit YAML. If you need the quantum graph, use
|
|
306
352
|
``bps submit``.
|
|
307
353
|
|
|
308
|
-
#. At the moment, the mechanism does not support transferring files other than
|
|
309
|
-
executable.
|
|
310
|
-
|
|
311
|
-
#. The script specified by ``customJob.executable`` is copied to the run's
|
|
312
|
-
submit directory and this copy (not the original script) is being submitted
|
|
313
|
-
for execution. As a result, making any changes to the original script after
|
|
314
|
-
the run has been submitted will have no effect even if the run is still in
|
|
315
|
-
the WMS work queue waiting for execution.
|
|
316
|
-
|
|
317
354
|
#. Some BPS plugins may require inclusion of plugin-specific settings for this
|
|
318
355
|
mechanism to work. Consult the documentation of the plugin you use for
|
|
319
356
|
details.
|
|
@@ -322,12 +359,32 @@ There are few things you need to keep in mind though:
|
|
|
322
359
|
and stderr of the job can be found same way as payload job. You also can use
|
|
323
360
|
``bps report`` to check its status, ``bps cancel`` to cancel it, etc.
|
|
324
361
|
|
|
325
|
-
_bps-
|
|
362
|
+
.. _bps-status:
|
|
326
363
|
|
|
327
364
|
Checking status
|
|
328
365
|
---------------
|
|
366
|
+
To check the status of a particular submitted run, use
|
|
329
367
|
|
|
330
|
-
|
|
368
|
+
.. code-block:: bash
|
|
369
|
+
|
|
370
|
+
bps status --id <wms id or path>
|
|
371
|
+
|
|
372
|
+
It will print a name of the status (e.g., SUCCEEDED) as well as
|
|
373
|
+
exit with a status value (e.g., 15 for RUNNING). For the full list
|
|
374
|
+
of status names and values see ``lsst.ctrl.bps.WmsStates``.
|
|
375
|
+
|
|
376
|
+
If wanting some timing information, turn on debugging logging as below.
|
|
377
|
+
Also check to see if WMS plugin can turn on more information.
|
|
378
|
+
|
|
379
|
+
.. code-block:: bash
|
|
380
|
+
|
|
381
|
+
bps --long-log --log-level=lsst.ctrl.bps.status=DEBUG status --id <wms id or path>
|
|
382
|
+
|
|
383
|
+
.. _bps-report:
|
|
384
|
+
|
|
385
|
+
Printing a report
|
|
386
|
+
-----------------
|
|
387
|
+
To get a summary of job statuses of submitted runs, use
|
|
331
388
|
|
|
332
389
|
.. code-block:: bash
|
|
333
390
|
|
|
@@ -789,7 +846,7 @@ Supported settings
|
|
|
789
846
|
When to output job QuantumGraph files (default = TRANSFORM).
|
|
790
847
|
|
|
791
848
|
* NEVER = all jobs will use full QuantumGraph file. (Warning: make sure
|
|
792
|
-
runQuantumCommand has ``--qgraph-
|
|
849
|
+
runQuantumCommand has ``--qgraph-node-id {qgraphNodeId}``.)
|
|
793
850
|
* TRANSFORM = Output QuantumGraph files after creating GenericWorkflow.
|
|
794
851
|
* PREPARE = QuantumGraph files are output after creating WMS submission.
|
|
795
852
|
|
|
@@ -847,7 +904,7 @@ Reserved keywords
|
|
|
847
904
|
However, contrary to YAML specification, it is currently not portable.
|
|
848
905
|
|
|
849
906
|
**qgraphId**
|
|
850
|
-
|
|
907
|
+
Ignored; accepted for backwards compatibility.
|
|
851
908
|
|
|
852
909
|
**qgraphNodeId**
|
|
853
910
|
Comma-separated list of internal QuantumGraph node numbers to be
|
|
@@ -1022,13 +1079,12 @@ single full QuantumGraph file plus node numbers for each job. The default is
|
|
|
1022
1079
|
using per-job QuantumGraph files.
|
|
1023
1080
|
|
|
1024
1081
|
To use full QuantumGraph file, the submit YAML must set ``whenSaveJobQgraph`` to
|
|
1025
|
-
"NEVER" and the ``pipetask run`` command must include ``--qgraph-id {
|
|
1026
|
-
--qgraph-node-id {qgraphNodeId}``. For example:
|
|
1082
|
+
"NEVER" and the ``pipetask run`` command must include ``--qgraph-node-id {qgraphNodeId}``. For example:
|
|
1027
1083
|
|
|
1028
1084
|
.. code::
|
|
1029
1085
|
|
|
1030
1086
|
whenSaveJobQgraph: "NEVER"
|
|
1031
|
-
runQuantumCommand: "${CTRL_MPEXEC_DIR}/bin/pipetask --long-log run -b {butlerConfig} --output {output} --output-run {outputRun} --qgraph {qgraphFile} --qgraph-
|
|
1087
|
+
runQuantumCommand: "${CTRL_MPEXEC_DIR}/bin/pipetask --long-log run -b {butlerConfig} --output {output} --output-run {outputRun} --qgraph {qgraphFile} --qgraph-node-id {qgraphNodeId} --skip-init-writes --extend-run --clobber-outputs --skip-existing"
|
|
1032
1088
|
|
|
1033
1089
|
|
|
1034
1090
|
.. warning::
|
|
@@ -1105,7 +1161,7 @@ New YAML Section
|
|
|
1105
1161
|
implementation: JOB
|
|
1106
1162
|
concurrencyLimit: db_limit
|
|
1107
1163
|
command1: >-
|
|
1108
|
-
${DAF_BUTLER_DIR}/bin/butler
|
|
1164
|
+
${DAF_BUTLER_DIR}/bin/butler aggregate-graph
|
|
1109
1165
|
{fileDistributionEndPoint}{qgraphFile}
|
|
1110
1166
|
{butlerConfig}
|
|
1111
1167
|
--register-dataset-types
|
|
@@ -1187,7 +1243,7 @@ The major differences to users are:
|
|
|
1187
1243
|
the output run in the provided pre-existing quantum graph.
|
|
1188
1244
|
- ``final_post_finalJob.out``: An internal file for debugging incorrect
|
|
1189
1245
|
reporting of final run status.
|
|
1190
|
-
- ``<qgraph_filename>_orig.
|
|
1246
|
+
- ``<qgraph_filename>_orig.qg``: A backup copy of the original
|
|
1191
1247
|
pre-existing quantum graph file that was used for submitting the run. Note
|
|
1192
1248
|
that this file will *not* be present in the submit directory if the
|
|
1193
1249
|
pipeline YAML specification was used during the submission instead.
|
|
@@ -1477,6 +1533,49 @@ invisible to the user. ``bps report`` will still show same labels and
|
|
|
1477
1533
|
total counts as without ordering. ``cancel`` and ``restart`` will still
|
|
1478
1534
|
work the same.
|
|
1479
1535
|
|
|
1536
|
+
.. _bps-config-generation:
|
|
1537
|
+
|
|
1538
|
+
Config Generation
|
|
1539
|
+
-----------------
|
|
1540
|
+
|
|
1541
|
+
In some rare use cases, the submit yaml depends upon what happened in
|
|
1542
|
+
previous runs (e.g., passing pipeline configuration values to the HiPS
|
|
1543
|
+
QuantumGraph generation command depending upon colors of generated outputs
|
|
1544
|
+
of previous run). One can wait until a run finishes, query the results,
|
|
1545
|
+
and then manually modify the submit yaml for the next run. To help make
|
|
1546
|
+
this easier to automate, two special mechanisms, ``bpsGenerateConfig`` and
|
|
1547
|
+
``bpsEval`` have been added to ``bps``. While different syntax, both take
|
|
1548
|
+
two pieces of information. The first piece describes what to import and
|
|
1549
|
+
execute and the second the parameters to pass which typically will
|
|
1550
|
+
be config variables (e.g., "{butlerConfig}").
|
|
1551
|
+
|
|
1552
|
+
``bpsGenerateConfig`` is a key/value pair where the function returns a
|
|
1553
|
+
Mapping to update the config. It can be used at the root level and can
|
|
1554
|
+
return nested dictionaries to replace values across multiple sections.
|
|
1555
|
+
It can also be used inside sections (e.g., inside a specific pipetask
|
|
1556
|
+
section). The function is not run when loading the config. Instead it
|
|
1557
|
+
is run during the initialization in the ``bps submit`` (before saving
|
|
1558
|
+
the config yaml to the submit directory and before running QuantumGraph
|
|
1559
|
+
generation). Example:
|
|
1560
|
+
|
|
1561
|
+
..code::
|
|
1562
|
+
|
|
1563
|
+
bpsGenerateConfig: "lsst.my.package.my_func_1('{butlerConfig}', param3='{output}')"
|
|
1564
|
+
|
|
1565
|
+
|
|
1566
|
+
``bpsEval`` is a placeholder in an submit yaml value. It is executed when
|
|
1567
|
+
the corresponding key is requested from the config. Its function needs
|
|
1568
|
+
to return a value whose string representation can replace ``bpsEval``. Example:
|
|
1569
|
+
|
|
1570
|
+
..code::
|
|
1571
|
+
|
|
1572
|
+
extraQgraphOptions: "--dataset-query-constraint off bpsEval(lsst.my.package.my_func_2, '{butlerConfig}', '{output}')
|
|
1573
|
+
|
|
1574
|
+
.. warning::
|
|
1575
|
+
|
|
1576
|
+
Quotes must be placed around variables that return strings (e.g., '{butlerConfig}').
|
|
1577
|
+
Forgetting the quotes typically results in an invalid syntax error.
|
|
1578
|
+
|
|
1480
1579
|
.. _bps-softlink:
|
|
1481
1580
|
|
|
1482
1581
|
WMS-id softlink
|
|
@@ -1636,3 +1735,4 @@ when installing an LSST package:
|
|
|
1636
1735
|
.. _ctrl_bps_panda: https://pipelines.lsst.io/modules/lsst.ctrl.bps.panda/index.html
|
|
1637
1736
|
.. _pipelines_check: https://github.com/lsst/pipelines_check
|
|
1638
1737
|
.. _lsst_bps_plugins: https://github.com/lsst/lsst_bps_plugins
|
|
1738
|
+
.. _
|
|
@@ -6,19 +6,20 @@ build-backend = "setuptools.build_meta"
|
|
|
6
6
|
name = "lsst-ctrl-bps"
|
|
7
7
|
requires-python = ">=3.11.0"
|
|
8
8
|
description = "Pluggable execution of workflow graphs from Rubin pipelines."
|
|
9
|
-
license =
|
|
9
|
+
license = "BSD-3-Clause OR GPL-3.0-or-later"
|
|
10
|
+
license-files = ["COPYRIGHT", "LICENSE", "bsd_license.txt", "gpl-v3.0.txt"]
|
|
10
11
|
readme = "README.md"
|
|
11
12
|
authors = [
|
|
12
13
|
{name="Rubin Observatory Data Management", email="dm-admin@lists.lsst.org"},
|
|
13
14
|
]
|
|
14
15
|
classifiers = [
|
|
15
16
|
"Intended Audience :: Science/Research",
|
|
16
|
-
"License :: OSI Approved :: BSD License",
|
|
17
17
|
"Operating System :: OS Independent",
|
|
18
18
|
"Programming Language :: Python :: 3",
|
|
19
19
|
"Programming Language :: Python :: 3.11",
|
|
20
20
|
"Programming Language :: Python :: 3.12",
|
|
21
21
|
"Programming Language :: Python :: 3.13",
|
|
22
|
+
"Programming Language :: Python :: 3.14",
|
|
22
23
|
"Topic :: Scientific/Engineering :: Astronomy",
|
|
23
24
|
]
|
|
24
25
|
keywords = ["lsst"]
|
|
@@ -49,7 +50,6 @@ where = ["python"]
|
|
|
49
50
|
|
|
50
51
|
[tool.setuptools]
|
|
51
52
|
zip-safe = true
|
|
52
|
-
license-files = ["COPYRIGHT", "LICENSE", "bsd_license.txt", "gpl-v3.0.txt"]
|
|
53
53
|
|
|
54
54
|
[tool.setuptools.package-data]
|
|
55
55
|
"lsst.ctrl.bps" = ["etc/*.yaml"]
|
|
@@ -44,6 +44,8 @@ from lsst.daf.butler import Config
|
|
|
44
44
|
from lsst.resources import ResourcePath
|
|
45
45
|
from lsst.utils import doImport
|
|
46
46
|
|
|
47
|
+
from .bps_utils import bps_eval
|
|
48
|
+
|
|
47
49
|
_LOG = logging.getLogger(__name__)
|
|
48
50
|
|
|
49
51
|
# Using lsst.daf.butler.Config to resolve possible includes.
|
|
@@ -417,4 +419,63 @@ class BpsConfig(Config):
|
|
|
417
419
|
if default != _NO_SEARCH_DEFAULT_VALUE:
|
|
418
420
|
opt["default"] = default
|
|
419
421
|
|
|
422
|
+
# check for bpsEval
|
|
423
|
+
value = re.sub(
|
|
424
|
+
r"bpsEval\(([^,)]+), ([^)]+)\)", lambda m: str(bps_eval(m.group(1), m.group(2))), value
|
|
425
|
+
)
|
|
426
|
+
if "bpsEval" in value:
|
|
427
|
+
raise ValueError(f"Unparsable bpsEval in '{value}'")
|
|
428
|
+
|
|
420
429
|
return value
|
|
430
|
+
|
|
431
|
+
def generate_config(self) -> None:
|
|
432
|
+
"""Update config with values generated by bpsGenerateConfig
|
|
433
|
+
entries.
|
|
434
|
+
"""
|
|
435
|
+
_LOG.debug("generate_config before: %s", self)
|
|
436
|
+
self._recursive_generate_config("", self)
|
|
437
|
+
_LOG.debug("generate_config after: %s", self)
|
|
438
|
+
|
|
439
|
+
def _recursive_generate_config(self, recursive_key: str, sub_config: Config) -> None:
|
|
440
|
+
"""Update config with values generated by bpsGenerateConfig
|
|
441
|
+
entries.
|
|
442
|
+
|
|
443
|
+
Parameters
|
|
444
|
+
----------
|
|
445
|
+
recursive_key : `str`
|
|
446
|
+
Corresponds to a new subconfig in which to search
|
|
447
|
+
and replace bpsGenerateConfig.
|
|
448
|
+
|
|
449
|
+
sub_config : `lsst.daf.butler.Config`
|
|
450
|
+
The nested config corresponding to the recursive_key.
|
|
451
|
+
|
|
452
|
+
Raises
|
|
453
|
+
------
|
|
454
|
+
ValueError
|
|
455
|
+
If bpsGenerateConfig value isn't parseable.
|
|
456
|
+
ImportError
|
|
457
|
+
If problems importing bpsGenerateConfig's method.
|
|
458
|
+
"""
|
|
459
|
+
_LOG.debug("recursive_key = '%s'", recursive_key)
|
|
460
|
+
genkey = "bpsGenerateConfig" # to make it easier to change
|
|
461
|
+
|
|
462
|
+
# Save to avoid dictionary changed size during iteration error.
|
|
463
|
+
orig_keys = list(sub_config)
|
|
464
|
+
for key in orig_keys:
|
|
465
|
+
value = Config.__getitem__(sub_config, key)
|
|
466
|
+
_LOG.debug("key = %s, type(value) = %s", key, type(value))
|
|
467
|
+
if isinstance(value, Config):
|
|
468
|
+
self._recursive_generate_config(f"{recursive_key}.{key}", value)
|
|
469
|
+
elif key == genkey:
|
|
470
|
+
value = self.replace_vars(value, {"searchobj": sub_config})
|
|
471
|
+
|
|
472
|
+
m = re.match(r"(\S+)\((.+)\)", value)
|
|
473
|
+
if m:
|
|
474
|
+
results = bps_eval(m.group(1), m.group(2))
|
|
475
|
+
del sub_config[genkey]
|
|
476
|
+
sub_config.update(results)
|
|
477
|
+
if recursive_key:
|
|
478
|
+
self[recursive_key] = sub_config
|
|
479
|
+
_LOG.debug("After config = %s", self)
|
|
480
|
+
else:
|
|
481
|
+
raise ValueError(f"Unparsable {genkey} value='{value}'")
|
|
@@ -27,7 +27,14 @@
|
|
|
27
27
|
|
|
28
28
|
"""Classes and functions used in reporting run status."""
|
|
29
29
|
|
|
30
|
-
__all__ = [
|
|
30
|
+
__all__ = [
|
|
31
|
+
"BaseRunReport",
|
|
32
|
+
"DetailedRunReport",
|
|
33
|
+
"ExitCodesReport",
|
|
34
|
+
"SummaryRunReport",
|
|
35
|
+
"compile_code_summary",
|
|
36
|
+
"compile_job_summary",
|
|
37
|
+
]
|
|
31
38
|
|
|
32
39
|
import abc
|
|
33
40
|
import logging
|
|
@@ -55,7 +62,7 @@ class BaseRunReport(abc.ABC):
|
|
|
55
62
|
|
|
56
63
|
def __eq__(self, other):
|
|
57
64
|
if isinstance(other, BaseRunReport):
|
|
58
|
-
return
|
|
65
|
+
return self._table.pformat() == other._table.pformat()
|
|
59
66
|
return False
|
|
60
67
|
|
|
61
68
|
def __len__(self):
|
|
@@ -195,7 +202,7 @@ class DetailedRunReport(BaseRunReport):
|
|
|
195
202
|
job_summary = run_report.job_summary
|
|
196
203
|
if job_summary is None:
|
|
197
204
|
id_ = run_report.global_wms_id if use_global_id else run_report.wms_id
|
|
198
|
-
self._msg = f"WARNING: Job summary for run '{id_}' not available, report
|
|
205
|
+
self._msg = f"WARNING: Job summary for run '{id_}' not available, report may be incomplete."
|
|
199
206
|
return
|
|
200
207
|
|
|
201
208
|
if by_label_expected:
|
|
@@ -231,44 +238,60 @@ class ExitCodesReport(BaseRunReport):
|
|
|
231
238
|
error handling from the wms service.
|
|
232
239
|
"""
|
|
233
240
|
|
|
234
|
-
def add(self, run_report, use_global_id=False):
|
|
241
|
+
def add(self, run_report: WmsRunReport, use_global_id: bool = False) -> None:
|
|
235
242
|
# Docstring inherited from the base class.
|
|
236
243
|
|
|
237
|
-
|
|
238
|
-
|
|
244
|
+
exit_code_summary = run_report.exit_code_summary
|
|
245
|
+
if not exit_code_summary:
|
|
246
|
+
id_ = run_report.global_wms_id if use_global_id else run_report.wms_id
|
|
247
|
+
self._msg = f"WARNING: Exit code summary for run '{id_}' not available, report may be incomplete."
|
|
248
|
+
return
|
|
249
|
+
|
|
250
|
+
warnings = []
|
|
251
|
+
|
|
252
|
+
# If available, use label ordering from the run summary as it should
|
|
253
|
+
# reflect the ordering of the pipetasks in the pipeline.
|
|
239
254
|
labels = []
|
|
240
255
|
if run_report.run_summary:
|
|
241
256
|
for part in run_report.run_summary.split(";"):
|
|
242
257
|
label, _ = part.split(":")
|
|
243
258
|
labels.append(label)
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
return
|
|
259
|
+
if not labels:
|
|
260
|
+
labels = sorted(exit_code_summary)
|
|
261
|
+
warnings.append("WARNING: Could not determine order of pipeline, instead sorted alphabetically.")
|
|
248
262
|
|
|
249
263
|
# Payload (e.g. pipetask) error codes:
|
|
250
264
|
# * 1: general failure,
|
|
251
265
|
# * 2: command line error (e.g. unknown command and/or option).
|
|
252
266
|
pyld_error_codes = {1, 2}
|
|
253
267
|
|
|
254
|
-
|
|
268
|
+
missing_labels = set()
|
|
255
269
|
for label in labels:
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
270
|
+
try:
|
|
271
|
+
exit_codes = exit_code_summary[label]
|
|
272
|
+
except KeyError:
|
|
273
|
+
missing_labels.add(label)
|
|
274
|
+
else:
|
|
275
|
+
pyld_errors = [code for code in exit_codes if code in pyld_error_codes]
|
|
276
|
+
pyld_error_count = len(pyld_errors)
|
|
277
|
+
pyld_error_summary = (
|
|
278
|
+
", ".join(sorted(str(code) for code in set(pyld_errors))) if pyld_errors else "None"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
infra_errors = [code for code in exit_codes if code not in pyld_error_codes]
|
|
282
|
+
infra_error_count = len(infra_errors)
|
|
283
|
+
infra_error_summary = (
|
|
284
|
+
", ".join(sorted(str(code) for code in set(infra_errors))) if infra_errors else "None"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
run = [label, pyld_error_count, pyld_error_summary, infra_error_count, infra_error_summary]
|
|
288
|
+
self._table.add_row(run)
|
|
289
|
+
if missing_labels:
|
|
290
|
+
warnings.append(
|
|
291
|
+
f"WARNING: Exit code summary was not available for job labels: {', '.join(missing_labels)}"
|
|
268
292
|
)
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
self._table.add_row(run)
|
|
293
|
+
if warnings:
|
|
294
|
+
self._msg = "\n".join(warnings)
|
|
272
295
|
|
|
273
296
|
def __str__(self):
|
|
274
297
|
alignments = ["<"] + [">"] * (len(self._table.colnames) - 1)
|
|
@@ -276,7 +299,7 @@ class ExitCodesReport(BaseRunReport):
|
|
|
276
299
|
return str("\n".join(lines))
|
|
277
300
|
|
|
278
301
|
|
|
279
|
-
def compile_job_summary(report: WmsRunReport) ->
|
|
302
|
+
def compile_job_summary(report: WmsRunReport) -> list[str]:
|
|
280
303
|
"""Add a job summary to the run report if necessary.
|
|
281
304
|
|
|
282
305
|
If the job summary is not provided, the function will attempt to compile
|
|
@@ -289,24 +312,89 @@ def compile_job_summary(report: WmsRunReport) -> None:
|
|
|
289
312
|
report : `lsst.ctrl.bps.WmsRunReport`
|
|
290
313
|
Information about a single run.
|
|
291
314
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
315
|
+
Returns
|
|
316
|
+
-------
|
|
317
|
+
warnings : `list` [`str`]
|
|
318
|
+
List of messages describing any non-critical issues encountered during
|
|
319
|
+
processing. Empty if none.
|
|
297
320
|
"""
|
|
321
|
+
warnings: list[str] = []
|
|
322
|
+
|
|
323
|
+
# If the job summary already exists, exit early.
|
|
298
324
|
if report.job_summary:
|
|
299
|
-
return
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
325
|
+
return warnings
|
|
326
|
+
|
|
327
|
+
if report.jobs:
|
|
328
|
+
job_summary = {}
|
|
329
|
+
by_label = group_jobs_by_label(report.jobs)
|
|
330
|
+
for label, job_group in by_label.items():
|
|
331
|
+
by_label_state = group_jobs_by_state(job_group)
|
|
332
|
+
_LOG.debug("by_label_state = %s", by_label_state)
|
|
333
|
+
counts = {state: len(jobs) for state, jobs in by_label_state.items()}
|
|
334
|
+
job_summary[label] = counts
|
|
335
|
+
report.job_summary = job_summary
|
|
336
|
+
else:
|
|
337
|
+
warnings.append("information about individual jobs not available")
|
|
338
|
+
|
|
339
|
+
return warnings
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def compile_code_summary(report: WmsRunReport) -> list[str]:
|
|
343
|
+
"""Add missing entries to the exit code summary if necessary.
|
|
344
|
+
|
|
345
|
+
A WMS plugin may exclude job labels for which there are no failures from
|
|
346
|
+
the exit code summary. The function will attempt to use the job summary,
|
|
347
|
+
if available, to add missing entries for these labels.
|
|
348
|
+
|
|
349
|
+
Parameters
|
|
350
|
+
----------
|
|
351
|
+
report : `lsst.ctrl.bps.WmsRunReport`
|
|
352
|
+
Information about a single run.
|
|
353
|
+
|
|
354
|
+
Returns
|
|
355
|
+
-------
|
|
356
|
+
warnings : `list` [`str`]
|
|
357
|
+
List of messages describing any non-critical issues encountered during
|
|
358
|
+
processing. Empty if none.
|
|
359
|
+
"""
|
|
360
|
+
warnings: list[str] = []
|
|
361
|
+
|
|
362
|
+
# If the job summary is not available, exit early.
|
|
363
|
+
if not report.job_summary:
|
|
364
|
+
return warnings
|
|
365
|
+
|
|
366
|
+
# A shallow copy is enough here because we won't be modifying the existing
|
|
367
|
+
# entries, only adding new ones if necessary.
|
|
368
|
+
exit_code_summary = dict(report.exit_code_summary) if report.exit_code_summary else {}
|
|
369
|
+
|
|
370
|
+
# Use the job summary to add the entries for labels with no failures
|
|
371
|
+
# *without* modifying already existing entries.
|
|
372
|
+
failure_summary = {label: states[WmsStates.FAILED] for label, states in report.job_summary.items()}
|
|
373
|
+
for label, count in failure_summary.items():
|
|
374
|
+
if count == 0:
|
|
375
|
+
exit_code_summary.setdefault(label, [])
|
|
376
|
+
|
|
377
|
+
# Check if there are any discrepancies between the data in the exit code
|
|
378
|
+
# summary and the job summary.
|
|
379
|
+
code_summary_labels = set(exit_code_summary)
|
|
380
|
+
failure_summary_labels = set(failure_summary)
|
|
381
|
+
mismatches = {
|
|
382
|
+
label
|
|
383
|
+
for label in failure_summary_labels & code_summary_labels
|
|
384
|
+
if len(exit_code_summary[label]) != failure_summary[label]
|
|
385
|
+
}
|
|
386
|
+
if mismatches:
|
|
387
|
+
warnings.append(
|
|
388
|
+
f"number of exit codes differs from number of failures for job labels: {', '.join(mismatches)}"
|
|
389
|
+
)
|
|
390
|
+
missing = failure_summary_labels - code_summary_labels
|
|
391
|
+
if missing:
|
|
392
|
+
warnings.append(f"exit codes not available for job labels: {', '.join(missing)}")
|
|
393
|
+
|
|
394
|
+
if exit_code_summary:
|
|
395
|
+
report.exit_code_summary = exit_code_summary
|
|
396
|
+
|
|
397
|
+
return warnings
|
|
310
398
|
|
|
311
399
|
|
|
312
400
|
def group_jobs_by_state(jobs):
|