lsst-pipe-base 30.2026.200__tar.gz → 30.2026.400__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.
Files changed (193) hide show
  1. {lsst_pipe_base-30.2026.200/python/lsst_pipe_base.egg-info → lsst_pipe_base-30.2026.400}/PKG-INFO +2 -1
  2. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/CHANGES.rst +66 -0
  3. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/creating-a-pipeline.rst +3 -3
  4. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/creating-a-pipelinetask.rst +1 -1
  5. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/creating-a-task.rst +1 -1
  6. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/index.rst +12 -0
  7. lsst_pipe_base-30.2026.400/doc/lsst.pipe.base/recording-provenance.rst +108 -0
  8. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/pyproject.toml +1 -0
  9. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/_instrument.py +10 -12
  10. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/_status.py +29 -10
  11. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/automatic_connection_constants.py +9 -1
  12. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/cli/cmd/__init__.py +16 -2
  13. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/cli/cmd/commands.py +42 -4
  14. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/connectionTypes.py +279 -0
  15. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/connections.py +3 -6
  16. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/execution_reports.py +0 -5
  17. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/log_capture.py +8 -4
  18. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/log_on_close.py +79 -0
  19. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/mp_graph_executor.py +51 -15
  20. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline.py +3 -4
  21. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipelineIR.py +0 -6
  22. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipelineTask.py +5 -7
  23. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/_edges.py +19 -7
  24. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/_pipeline_graph.py +8 -0
  25. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/_common.py +7 -4
  26. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/_multiblock.py +6 -16
  27. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/_predicted.py +111 -10
  28. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/_provenance.py +727 -26
  29. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/aggregator/_communicators.py +26 -50
  30. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/aggregator/_config.py +78 -9
  31. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/aggregator/_ingester.py +12 -11
  32. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/quantum_graph/aggregator/_scanner.py +325 -0
  33. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/quantum_graph/aggregator/_structs.py +67 -0
  34. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/aggregator/_supervisor.py +24 -18
  35. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/quantum_graph/aggregator/_writer.py +184 -0
  36. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/quantum_graph/formatter.py +171 -0
  37. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/quantum_graph/ingest_graph.py +356 -0
  38. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/quantum_graph_executor.py +229 -0
  39. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_provenance_graph.py +17 -2
  40. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/separable_pipeline_executor.py +18 -2
  41. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/single_quantum_executor.py +59 -41
  42. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/struct.py +4 -0
  43. lsst_pipe_base-30.2026.400/python/lsst/pipe/base/version.py +2 -0
  44. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400/python/lsst_pipe_base.egg-info}/PKG-INFO +2 -1
  45. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst_pipe_base.egg-info/SOURCES.txt +4 -0
  46. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_aggregator.py +179 -167
  47. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_separable_pipeline_executor.py +101 -13
  48. lsst_pipe_base-30.2026.200/python/lsst/pipe/base/connectionTypes.py +0 -367
  49. lsst_pipe_base-30.2026.200/python/lsst/pipe/base/quantum_graph/aggregator/_scanner.py +0 -511
  50. lsst_pipe_base-30.2026.200/python/lsst/pipe/base/quantum_graph/aggregator/_structs.py +0 -177
  51. lsst_pipe_base-30.2026.200/python/lsst/pipe/base/quantum_graph/aggregator/_writer.py +0 -501
  52. lsst_pipe_base-30.2026.200/python/lsst/pipe/base/quantum_graph_executor.py +0 -126
  53. lsst_pipe_base-30.2026.200/python/lsst/pipe/base/version.py +0 -2
  54. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/COPYRIGHT +0 -0
  55. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/LICENSE +0 -0
  56. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/MANIFEST.in +0 -0
  57. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/README.md +0 -0
  58. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/bsd_license.txt +0 -0
  59. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/task-framework-overview.rst +0 -0
  60. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/task-retargeting-howto.rst +0 -0
  61. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/testing-a-pipeline-task.rst +0 -0
  62. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/testing-pipelines-with-mocks.rst +0 -0
  63. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/doc/lsst.pipe.base/working-with-pipeline-graphs.rst +0 -0
  64. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/gpl-v3.0.txt +0 -0
  65. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/__init__.py +0 -0
  66. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/__init__.py +0 -0
  67. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/__init__.py +0 -0
  68. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/_datasetQueryConstraints.py +0 -0
  69. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/_dataset_handle.py +0 -0
  70. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/_observation_dimension_packer.py +0 -0
  71. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/_quantumContext.py +0 -0
  72. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/_task_metadata.py +0 -0
  73. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/all_dimensions_quantum_graph_builder.py +0 -0
  74. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/caching_limited_butler.py +0 -0
  75. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/cli/__init__.py +0 -0
  76. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/cli/_get_cli_subcommands.py +0 -0
  77. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/cli/opt/__init__.py +0 -0
  78. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/cli/opt/arguments.py +0 -0
  79. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/cli/opt/options.py +0 -0
  80. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/config.py +0 -0
  81. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/configOverrides.py +0 -0
  82. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/dot_tools.py +0 -0
  83. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/exec_fixup_data_id.py +0 -0
  84. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/execution_graph_fixup.py +0 -0
  85. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/formatters/__init__.py +0 -0
  86. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/formatters/pexConfig.py +0 -0
  87. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/graph/__init__.py +0 -0
  88. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/graph/_implDetails.py +0 -0
  89. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/graph/_loadHelpers.py +0 -0
  90. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/graph/_versionDeserializers.py +0 -0
  91. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/graph/graph.py +0 -0
  92. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/graph/graphSummary.py +0 -0
  93. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/graph/quantumNode.py +0 -0
  94. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/graph_walker.py +0 -0
  95. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/mermaid_tools.py +0 -0
  96. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/__init__.py +0 -0
  97. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/__main__.py +0 -0
  98. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/_dataset_types.py +0 -0
  99. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/_exceptions.py +0 -0
  100. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/_mapping_views.py +0 -0
  101. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/_nodes.py +0 -0
  102. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/_task_subsets.py +0 -0
  103. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/_tasks.py +0 -0
  104. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/expressions.py +0 -0
  105. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/io.py +0 -0
  106. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/__init__.py +0 -0
  107. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_dot.py +0 -0
  108. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py +0 -0
  109. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_layout.py +0 -0
  110. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_merge.py +0 -0
  111. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_mermaid.py +0 -0
  112. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_options.py +0 -0
  113. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_printer.py +0 -0
  114. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_show.py +0 -0
  115. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/pipeline_graph/visualization/_status_annotator.py +0 -0
  116. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/prerequisite_helpers.py +0 -0
  117. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/py.typed +0 -0
  118. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/__init__.py +0 -0
  119. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/aggregator/__init__.py +0 -0
  120. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/aggregator/_progress.py +0 -0
  121. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph/visualization.py +0 -0
  122. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph_builder.py +0 -0
  123. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_graph_skeleton.py +0 -0
  124. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/quantum_reports.py +0 -0
  125. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/resource_usage.py +0 -0
  126. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/script/__init__.py +0 -0
  127. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/script/register_instrument.py +0 -0
  128. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/script/retrieve_artifacts_for_quanta.py +0 -0
  129. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/script/transfer_from_graph.py +0 -0
  130. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/script/utils.py +0 -0
  131. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/script/zip_from_graph.py +0 -0
  132. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/simple_pipeline_executor.py +0 -0
  133. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/task.py +0 -0
  134. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/taskFactory.py +0 -0
  135. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/testUtils.py +0 -0
  136. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/__init__.py +0 -0
  137. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/in_memory_limited_butler.py +0 -0
  138. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/mocks/__init__.py +0 -0
  139. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/mocks/_data_id_match.py +0 -0
  140. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/mocks/_pipeline_task.py +0 -0
  141. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/mocks/_repo.py +0 -0
  142. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/mocks/_storage_class.py +0 -0
  143. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/no_dimensions.py +0 -0
  144. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/pipelineStepTester.py +0 -0
  145. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/simpleQGraph.py +0 -0
  146. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/tests/util.py +0 -0
  147. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst/pipe/base/utils.py +0 -0
  148. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst_pipe_base.egg-info/dependency_links.txt +0 -0
  149. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst_pipe_base.egg-info/entry_points.txt +0 -0
  150. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst_pipe_base.egg-info/requires.txt +0 -0
  151. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst_pipe_base.egg-info/top_level.txt +0 -0
  152. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/python/lsst_pipe_base.egg-info/zip-safe +0 -0
  153. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/setup.cfg +0 -0
  154. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_adjust_all_quanta.py +0 -0
  155. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_caching_limited_butler.py +0 -0
  156. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_cliCmdRegisterInstrument.py +0 -0
  157. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_configOverrides.py +0 -0
  158. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_config_formatter.py +0 -0
  159. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_connections.py +0 -0
  160. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_dataid_match.py +0 -0
  161. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_dataset_handle.py +0 -0
  162. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_deferredDatasetRef.py +0 -0
  163. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_dot_tools.py +0 -0
  164. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_dynamic_connections.py +0 -0
  165. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_execution_reports.py +0 -0
  166. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_execution_storage_class_conversion.py +0 -0
  167. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_graphBuilder.py +0 -0
  168. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_graph_walker.py +0 -0
  169. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_init_output_run.py +0 -0
  170. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_instrument.py +0 -0
  171. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_mermaid.py +0 -0
  172. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_mp_graph_executor.py +0 -0
  173. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_pipeline.py +0 -0
  174. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_pipelineIR.py +0 -0
  175. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_pipelineLoadSubset.py +0 -0
  176. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_pipelineTask.py +0 -0
  177. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_pipeline_graph.py +0 -0
  178. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_pipeline_graph_expressions.py +0 -0
  179. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_predicted_qg.py +0 -0
  180. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_qg_builder_dimensions.py +0 -0
  181. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_quantumGraph.py +0 -0
  182. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_quantum_provenance_graph.py +0 -0
  183. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_quantum_reports.py +0 -0
  184. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_quantum_success_caveats.py +0 -0
  185. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_script_utils.py +0 -0
  186. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_simple_pipeline_executor.py +0 -0
  187. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_single_quantum_executor.py +0 -0
  188. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_struct.py +0 -0
  189. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_task.py +0 -0
  190. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_task_factory.py +0 -0
  191. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_taskmetadata.py +0 -0
  192. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_testUtils.py +0 -0
  193. {lsst_pipe_base-30.2026.200 → lsst_pipe_base-30.2026.400}/tests/test_utils.py +0 -0
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-pipe-base
3
- Version: 30.2026.200
3
+ Version: 30.2026.400
4
4
  Summary: Pipeline infrastructure for the Rubin Science Pipelines.
5
5
  Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
6
  License-Expression: BSD-3-Clause OR GPL-3.0-or-later
7
7
  Project-URL: Homepage, https://github.com/lsst/pipe_base
8
+ Project-URL: Source, https://github.com/lsst/pipe_base
8
9
  Keywords: lsst
9
10
  Classifier: Intended Audience :: Science/Research
10
11
  Classifier: Operating System :: OS Independent
@@ -1,3 +1,69 @@
1
+ lsst-pipe-base v30.0.0 (2026-01-16)
2
+ ===================================
3
+
4
+ New Features
5
+ ------------
6
+
7
+ - Added support for healpix (and other non-database dimensions) in quantum graph builder. (`DM-51176 <https://rubinobs.atlassian.net/browse/DM-51176>`_)
8
+ - Added filtering out dataset refs that the destination butler already knows in ``transfer_from_graph`` as well as dividing the transfer into smaller chunks to speed up restarts. (`DM-51273 <https://rubinobs.atlassian.net/browse/DM-51273>`_)
9
+ - Added handling of ``PrerequisiteInput`` in ``QuantaAdjuster``, with a corresponding unit test. (`DM-51509 <https://rubinobs.atlassian.net/browse/DM-51509>`_)
10
+ - Added ``PredictedQuantumGraph``, a replacement for the ``QuantumGraph`` class with more efficient I/O (via a new file format and more partial-read flexibility).
11
+
12
+ The new ``PredictedQuantumGraph`` is now the default in most tooling, and the new format can be opted into via the ``.qg`` (instead of ``.qgraph``) file extension.
13
+ New files can be read with the old class and vice versa.
14
+
15
+ The ``QuantumGraph`` class will eventually be deprecated along with much of the current provenance reporting tooling, but only when the new provenance ecosystem is fully in place. (`DM-51850 <https://rubinobs.atlassian.net/browse/DM-51850>`_)
16
+ - Added a ``rename`` dict in ``ImportIR`` to support renaming task labels, with corresponding unit tests. (`DM-52168 <https://rubinobs.atlassian.net/browse/DM-52168>`_)
17
+ - Added the new ``ProvenanceQuantumGraph`` class and the ``aggregate-graph`` tool (a replacement for ``transfer-from-graph``) that writes it at the end of batch runs. (`DM-52360 <https://rubinobs.atlassian.net/browse/DM-52360>`_)
18
+ - Improved provenance tracking for failed quanta and retries.
19
+
20
+ By storing extra information in the log datasets written during execution,
21
+ we can record caught exceptions, track which other quanta have
22
+ already executed in the same process, and keep track of previous attempts to
23
+ run the same quantum. (`DM-53019 <https://rubinobs.atlassian.net/browse/DM-53019>`_)
24
+ - Added provenance writing support to ``MPGraphExecutor`` and ``SeparablePipelineExecutor``. (`DM-53622 <https://rubinobs.atlassian.net/browse/DM-53622>`_)
25
+
26
+
27
+ API Changes
28
+ -----------
29
+
30
+ - Moved pipeline executors and their support code here, from ``lsst.ctrl.mpexec``.
31
+
32
+ This included minor API changes for ``SingleQuantumExecutor`` as well: consistent snake-case naming, keyword-only arguments for construction, and a switch to private instance attributes. (`DM-48980 <https://rubinobs.atlassian.net/browse/DM-48980>`_)
33
+
34
+
35
+ Bug Fixes
36
+ ---------
37
+
38
+ - Fixed ``transfer-from-graph`` to update chain when asked and output run collection exists even if didn't transfer any datasets. (`DM-51821 <https://rubinobs.atlassian.net/browse/DM-51821>`_)
39
+ - Fixed bug in ``transfer_from_graph`` where the input collections were not flattened before adding to a new output chain. (`DM-52004 <https://rubinobs.atlassian.net/browse/DM-52004>`_)
40
+ - Fixed bug where the log's ``MDC.RUN`` was the empty string when using a quantum-backed butler. (`DM-52676 <https://rubinobs.atlassian.net/browse/DM-52676>`_)
41
+ - Fixed a bug that caused ``PipelineGraph`` objects to be marked as unresolved when loaded from disk. (`DM-52787 <https://rubinobs.atlassian.net/browse/DM-52787>`_)
42
+
43
+
44
+ Other Changes and Additions
45
+ ---------------------------
46
+
47
+ - The ``Instrument.configPaths`` property can now refer to ``lsst.resources.ResourcePath`` URIs as well as strings (paths or URI strings). (`DM-33226 <https://rubinobs.atlassian.net/browse/DM-33226>`_)
48
+ - Updated pipe_base code to use the constants defined in ``automatic_connection_constants.py``. (`DM-52676 <https://rubinobs.atlassian.net/browse/DM-52676>`_)
49
+ - Uses UUIDs instead of internal integer IDs in new quantum graph storage.
50
+
51
+ This includes backwards compatibility read support for predicted quantum graphs, but not provenance quantum graphs, because those are still experimental anyway.
52
+
53
+ This increases the size of the files by ~6%, but it simplifies the codebase and will make consolidating multiple small provenance quantum graphs (as we
54
+ currently anticipate doing for prompt processing) much more efficient. (`DM-53174 <https://rubinobs.atlassian.net/browse/DM-53174>`_)
55
+ - Used context managers to ensure that database resources are freed. (`DM-53370 <https://rubinobs.atlassian.net/browse/DM-53370>`_)
56
+ - Added more logging for the later steps of QG building. (`DM-53636 <https://rubinobs.atlassian.net/browse/DM-53636>`_)
57
+
58
+
59
+ An API Removal or Deprecation
60
+ -----------------------------
61
+
62
+ - Removes the ``buildExecutionButler`` function and all supporting code.
63
+
64
+ Execution butlers (read-only SQLite databases used for batch execution) have been fully superseded by ``lsst.daf.butler.QuantumBackedButler``. (`DM-52044 <https://rubinobs.atlassian.net/browse/DM-52044>`_)
65
+
66
+
1
67
  lsst-pipe-base v29.1.0 (2025-06-13)
2
68
  ===================================
3
69
 
@@ -150,12 +150,12 @@ associated with ``class`` keyword instead of the label directly. The
150
150
  the configuration appropriate for this `Pipeline` specified as an additional
151
151
  yaml mapping.
152
152
 
153
- The complete complexity of :ref:`lsst.pex.config` can't be represented with simple
153
+ The complete complexity of `lsst.pex.config` can't be represented with simple
154
154
  yaml mapping syntax. To account for this, ``config`` blocks in `Pipeline`\ s
155
155
  support two special fields: ``file`` and ``python``.
156
156
 
157
157
  The ``file`` key may be associated with either a single value pointing to a
158
- filesystem path where a :ref:`lsst.pex.config` file can be found, or a yaml list
158
+ filesystem path where a `lsst.pex.config` file can be found, or a yaml list
159
159
  of such paths. The file paths can contain environment variables that will be
160
160
  expanded prior to loading the file(s). These files will then be applied to
161
161
  the task during configuration time to override any default values.
@@ -477,7 +477,7 @@ desired camera, or can serve as a base for further `Pipeline`\ s to import.
477
477
  Command line options for running Pipelines
478
478
  ------------------------------------------
479
479
  This section is not intended to serve as a tutorial for processing data from
480
- the command line, for that refer to :ref:`lsst.ctrl.mpexec` or :ref:`lsst.ctrl.bps`.
480
+ the command line, for that refer to `lsst.ctrl.mpexec` or `lsst.ctrl.bps`.
481
481
  However, both of these tools accept URI pointers to a `Pipeline`. These URIs
482
482
  can be altered with a specific syntax which will control how the `Pipeline`
483
483
  is loaded.
@@ -142,7 +142,7 @@ not tied to the exact band passes of an individual telescope filter).
142
142
 
143
143
  Next, take a look at the fields defined on your new connection class. These
144
144
  are defined in a similar way as defining a configuration class, but instead
145
- of using `~lsst.pex.config.Field` types from :ref:`lsst.pex.config`,
145
+ of using `~lsst.pex.config.Field` types from `lsst.pex.config`,
146
146
  connection classes make use of connection types defined in
147
147
  :py:mod:`lsst.pipe.base.connectionTypes`. These connections define the inputs and outputs that
148
148
  a |PipelineTask| will expect to make use of. Each of these connections documents
@@ -145,7 +145,7 @@ Use the ``__init__`` method (task constructor) to do the following:
145
145
 
146
146
  - Call the parent task's ``__init__`` method
147
147
  - Make subtasks by calling ``self.makeSubtask(name)``, where ``name`` is the name of a field of type `lsst.pex.config.ConfigurableField` in your :ref:`task's configuration <creating-a-task-configuration>`.
148
- - Make a schema if your task uses an :ref:`lsst.afw.table`.
148
+ - Make a schema if your task uses an `lsst.afw.table`.
149
149
  For an example of such a task `lsst.pipe.tasks.calibrate.CalibrateTask`.
150
150
  - Initialize any other instance variables your task needs.
151
151
 
@@ -61,6 +61,14 @@ Developing Pipelines
61
61
  testing-pipelines-with-mocks.rst
62
62
  working-with-pipeline-graphs.rst
63
63
 
64
+ Running Pipelines
65
+ -----------------
66
+
67
+ .. toctree::
68
+ :maxdepth: 1
69
+
70
+ recording-provenance.rst
71
+
64
72
  .. _lsst.pipe.base-contributing:
65
73
 
66
74
  Contributing
@@ -102,6 +110,10 @@ Python API reference
102
110
 
103
111
  .. automodapi:: lsst.pipe.base.quantum_graph
104
112
 
113
+ .. automodapi:: lsst.pipe.base.quantum_graph.aggregator
114
+
115
+ .. automodapi:: lsst.pipe.base.quantum_graph.ingest_graph
116
+
105
117
  .. automodapi:: lsst.pipe.base.quantum_graph.visualization
106
118
 
107
119
  QuantumGraph generation API reference
@@ -0,0 +1,108 @@
1
+ .. _pipe_base_provenance:
2
+
3
+ .. py:currentmodule:: lsst.pipe.base.quantum_graph
4
+
5
+ ####################
6
+ Recording Provenance
7
+ ####################
8
+
9
+ The `PredictedQuantumGraph` that is used to predict and control processing also contains a wealth of provenance information, including task configuration and the complete input-output relationships between all datasets.
10
+ Instead of storing these graphs directly in a `~lsst.daf.butler.Butler` repository, however, it is better to first augment them with additional provenance information that is only available after execution has completed, producing a `ProvenanceQuantumGraph` that is ingested instead.
11
+ We store provenance in a ``run_provenance`` dataset type with empty dimensions, which means there is exactly one for each `~lsst.daf.butler.CollectionType.RUN` collection.
12
+ In addition to the input-output graph itself and full configuration for all tasks, `ProvenanceQuantumGraph` stores status information for each attempt to run a quantum, including exception information and caveats on any successes.
13
+ It can also store the full logs and task metadata for each quantum, allowing repositories to store many fewer small files (it is possible to continue to have per-quantum butler datasets for these, all backed by the same file).
14
+
15
+ The pipeline system has many different execution contexts, and provenance recording is not supported in all of them at this time.
16
+
17
+ Batch Execution / Quantum-Backed Butler
18
+ =======================================
19
+
20
+ Provenance recording is fully supported in batch workflows that use the `~lsst.daf.butler.QuantumBackedButler` class (e.g. ``pipetask run-qbb``, as run by the ``bps`` tool) to avoid database writes during execution.
21
+ This involves the following steps:
22
+
23
+ - A `PredictedQuantumGraph` is generated as usual (e.g. via ``pipetask qgraph``, as run by ``bps submit``) and saved to a known location.
24
+ - All quanta are executed via ``pipetask run-qbb``, writing their outputs to butler-managed storage without updating the butler database.
25
+ - When all quanta have been attempted, the ``butler aggregate-graph`` tool is run (e.g. in the BPS ``finalJob``) to ingest output datasets into the butler database, and the ``--output`` option is used to save a `ProvenanceQuantumGraph` to a known location.
26
+ This step and the previous one may be run multiple times (e.g. via ``bps restart``) to retry some failures, and it is only necessary to pass ``--output`` the last time (though usually the user does not know which attempt will be the last one).
27
+ - When all processing attempts are complete, the ``butler ingest-graph`` tool is used to ingest the graph into the butler database and rewrite all metadata, log, and config datasets to also be backed by the same graph file (deleting the original files).
28
+ This step should not be included in the BPS ``finalJob`` (see below).
29
+
30
+ All of the above happens in a single `~lsst.daf.butler.CollectionType.RUN` collection.
31
+ Reference documentation for ``butler aggregate-graph`` and ``butler ingest-graph`` can be found in the `aggregator` and `ingest_graph` modules that implement them (respectively); in both cases there are Python interfaces that closely mirror the command-line ones.
32
+
33
+ Parallelization
34
+ ---------------
35
+
36
+ Aggregating and ingesting a large batch run is expensive, and both tools use parallelism whenever possible to improve performance.
37
+
38
+ The aggregator in particular is explicitly parallel, with separate workers (usually subprocesses) assigned to scan and read metadata and log files (any number of workers), ingest datasets (a single worker), write the provenance graph file (a single worker), and coordinate all of these operations.
39
+ Since all information must be passed from the scanners to the ingestion and writer workers, additional parallelism can help when all operations are running at around the same speed (as reported in the logs), but not when ingestion or writing lags significantly behind.
40
+ The writer process has substantial startup overhead and will typically lag the others at the beginning before it catches up later.
41
+
42
+ The `ingest_graph` tool mostly performs database write operations, which do not benefit from parallelism, but it also deletes the original metadata, log, and config files as the new graph-backed variants of those datasets are ingested.
43
+ These deletes are delegated to `lsst.resources.ResourcePath.mremove`, which refers to the ``LSST_RESOURCES_NUM_WORKERS``, ``LSST_RESOURCES_EXECUTOR``, and ``LSST_S3_USE_THREADS`` environment variables to control parallelism.
44
+ As with other butler bulk-delete operations, the default parallelism is usually fine.
45
+
46
+ .. note::
47
+
48
+ Earlier versions of the `aggregator` would run catastrophically slowly when ``LSST_RESOURCES_EXECUTOR=process``, as this made each scanner process spawn multiple subprocesses constantly.
49
+ In recent versions all parallelism environment variables are ignored by the aggregator so this should not occur.
50
+
51
+ Ingesting Outputs Early
52
+ -----------------------
53
+
54
+ The `aggregator` may be run with `~aggregator.AggregatorConfig.incomplete` set to `True` (``--incomplete`` on the command line) to allow it to be safely run before the graph has finished executing.
55
+ Note that while ingestion always picks up where it left off, scanning always has to start at the beginning, and provenance graph writing is disabled when running in ``incomplete`` mode, so while this allows output datasets be be available via the `~lsst.daf.butler.Butler` sooner, it does not generally make the final complete `aggregator` call substantially faster.
56
+
57
+ Promising Graph Ingestion
58
+ -------------------------
59
+
60
+ By default, the `aggregator` ingests all metadata, log, and config outputs into the butler database in the usual way, i.e. backed by their original individual files.
61
+ The `ingest_graph` tool then has to delete these datasets from the butler database before it can ingest new ones and delete the original files.
62
+ When it is known in advance that `ingest_graph` will be run later, the `~aggregator.AggregatorConfig.promise_ingest_graph` (``--promise-ingest-graph``) option can be used to tell the `aggregator` *not* to ingest these, saving time for both commands.
63
+ This option must be used with care, however: if `ingest_graph` isn't run later, the original files will be orphaned in a butler-managed location without any record in the database, which generally means they'll quietly take up space.
64
+ In addition, because the metadata datasets are used by the middleware system as the indicator of a quantum's success, their absence will make any downstream quantum graphs built using ``--skip-existing-in`` incorrect.
65
+ And of course any downstream quantum graph builds that actually use those datasets as input (only metadata should be) will not see them as available.
66
+
67
+ Deferring Graph Ingestion
68
+ -------------------------
69
+
70
+ Ingesting the provenance graph is not generally necessary to kick off downstream processing by building new quantum graphs for later pipeline steps, and it is always safe to build downstream quantum graphs if `~aggregator.AggregatorConfig.promise_ingest_graph` is left `False`.
71
+ It can also be done safely if `~aggregator.AggregatorConfig.promise_ingest_graph` is `True` and:
72
+
73
+ - ``--skip-existing-in`` is not used;
74
+ - the downstream processing does not use metadata, log, or config datasets as an overall input (``pipetask build ... --show inputs`` can be used to check for this).
75
+
76
+ These conditions also must be met in order for `ingest_graph` to be safely run *while* a downstream quantum graph is being executed.
77
+ Both of these conditions are *usually* met, and deferring and promising graph ingest each provide significant wall-clock savings, so we recommend the following approach for very large BPS campaigns:
78
+
79
+ - Submit ``step(N)`` to BPS with ``--promise-ingest-graph`` in the ``finalJob`` invocation of ``aggregate-graph``.
80
+ - When ready to move on to ``step(N+1)``, run ``pipetask build ... --show inputs`` (on ``step(N+1)``) to scan for metadata, log, and config inputs that may be needed from the previous step.
81
+ - If there are no such inputs, immediately submit that step to BPS, and run `ingest_graph` on ``step(N)`` as soon as the quantum graph for ``step(N+1)`` is built (it could be built at the same time, but waiting a bit may help spread out database load).
82
+ - If there are metadata, log, or config inputs, run `ingest_graph` on ``step(N)`` and wait for it to finish before submitting ``step(N+1)``.
83
+
84
+ Note that *independent* quantum graph builds (e.g. same tasks, disjoint data IDs) can always be built before or while `ingest_graph` runs.
85
+
86
+ Recovering from Interruptions
87
+ -----------------------------
88
+
89
+ If the `aggregator` is interrupted it can simply be started again.
90
+ Database ingestion will pick up where it left off, while scanning and provenance-graph writing will start over from the beginning.
91
+
92
+ If `ingest_graph` is interrupted, it can also be started again, and everything will pick up where it left off.
93
+ To guarantee this it always modifies the repository in the following order:
94
+
95
+ - if the ``run_provenance`` dataset does not exist in the collection, all existing metadata/log/config datasets are assumed to be backed by their original files and are deleted from the butler database (without deleting the files);
96
+ - the ``run_provenance`` dataset itself is ingested (this ensures the metadata/log/config *content* is safe inside the butler, even if it's not fully accessible);
97
+ - in batches, metadata/log/config datasets are reingested into the butler backed by the graph file, and then the corresponding original files are deleted.
98
+
99
+ This means we can use the existence of ``run_provenance`` and any particular metadata/log/config dataset in the butler database to infer the status of the original files.
100
+
101
+ In fact, if `ingest_graph` is interrupted at any point, it *must* be tried again until it succeeds, since not doing so can leave metadata/log/config files orphaned, just like when `~aggregator.AggregatorConfig.promise_ingest_graph` is `True`.
102
+
103
+ .. note::
104
+
105
+ After the ``run_provenance`` dataset is ingested, it is *not* safe to run the `aggregator`: the `aggregator` reads the original metadata and log files to gather provenance information, and will infer the wrong states for quanta if those are missing because `ingest_graph` has deleted them.
106
+
107
+ This is why it is not safe to run ``bps restart`` after `ingest_graph`, and why we do not recommend adding `ingest_graph` to the BPS ``finalJob``, even if the user is willing to forgo using ``bps restart``: by default, the ``finalJob`` will be retried on failure, causing the `aggregator` to run again when it may not be safe to do so.
108
+ And if ``finalJob`` retries are disabled, it is too easy for the repository to end up in a state that would require manual `ingest_graph` runs to prevent orphan datasets.
@@ -45,6 +45,7 @@ pipe_base = "lsst.pipe.base.cli:get_cli_subcommands"
45
45
 
46
46
  [project.urls]
47
47
  "Homepage" = "https://github.com/lsst/pipe_base"
48
+ "Source" = "https://github.com/lsst/pipe_base"
48
49
 
49
50
  [project.optional-dependencies]
50
51
  test = ["pytest >= 3.2"]
@@ -31,7 +31,6 @@ __all__ = ("Instrument",)
31
31
 
32
32
  import contextlib
33
33
  import datetime
34
- import os.path
35
34
  from abc import ABCMeta, abstractmethod
36
35
  from collections.abc import Sequence
37
36
  from typing import TYPE_CHECKING, Any, Self, cast, final
@@ -39,6 +38,7 @@ from typing import TYPE_CHECKING, Any, Self, cast, final
39
38
  from lsst.daf.butler import DataCoordinate, DataId, DimensionPacker, DimensionRecord, Formatter
40
39
  from lsst.daf.butler.registry import DataIdError
41
40
  from lsst.pex.config import Config, RegistryField
41
+ from lsst.resources import ResourcePath, ResourcePathExpression
42
42
  from lsst.utils import doImportType
43
43
  from lsst.utils.introspection import get_full_type_name
44
44
 
@@ -65,7 +65,7 @@ class Instrument(metaclass=ABCMeta):
65
65
  the base class.
66
66
  """
67
67
 
68
- configPaths: Sequence[str] = ()
68
+ configPaths: Sequence[ResourcePathExpression] = ()
69
69
  """Paths to config files to read for specific Tasks.
70
70
 
71
71
  The paths in this list should contain files of the form `task.py`, for
@@ -109,6 +109,10 @@ class Instrument(metaclass=ABCMeta):
109
109
  If `True` (`False` is default), update existing records if they
110
110
  differ from the new ones.
111
111
 
112
+ Returns
113
+ -------
114
+ None
115
+
112
116
  Raises
113
117
  ------
114
118
  lsst.daf.butler.registry.ConflictingDefinitionError
@@ -127,13 +131,6 @@ class Instrument(metaclass=ABCMeta):
127
131
  the level of individual dimension entries; new detectors and filters
128
132
  should be added, but changes to any existing record should not be.
129
133
  This can generally be achieved via a block like
130
-
131
- .. code-block:: python
132
-
133
- with registry.transaction():
134
- registry.syncDimensionData("instrument", ...)
135
- registry.syncDimensionData("detector", ...)
136
- self.registerFilters(registry)
137
134
  """
138
135
  raise NotImplementedError()
139
136
 
@@ -366,9 +363,10 @@ class Instrument(metaclass=ABCMeta):
366
363
  Config instance to which overrides should be applied.
367
364
  """
368
365
  for root in self.configPaths:
369
- path = os.path.join(root, f"{name}.py")
370
- if os.path.exists(path):
371
- config.load(path)
366
+ resource = ResourcePath(root, forceDirectory=True, forceAbsolute=True)
367
+ uri = resource.join(f"{name}.py", forceDirectory=False)
368
+ if uri.exists():
369
+ config.load(uri)
372
370
 
373
371
  @staticmethod
374
372
  def formatCollectionTimestamp(timestamp: str | datetime.datetime) -> str:
@@ -275,15 +275,23 @@ class ExceptionInfo(pydantic.BaseModel):
275
275
  class QuantumAttemptStatus(enum.Enum):
276
276
  """Enum summarizing an attempt to run a quantum."""
277
277
 
278
+ ABORTED = -4
279
+ """The quantum failed with a hard error that prevented both logs and
280
+ metadata from being written.
281
+
282
+ This state is only set if information from higher-level tooling (e.g. BPS)
283
+ is available to distinguish it from ``UNKNOWN``.
284
+ """
285
+
278
286
  UNKNOWN = -3
279
287
  """The status of this attempt is unknown.
280
288
 
281
- This usually means no logs or metadata were written, and it at least could
282
- not be determined whether the quantum was blocked by an upstream failure
283
- (if it was definitely blocked, `BLOCKED` is set instead).
289
+ This means no logs or metadata were written, and it at least could not be
290
+ determined whether the quantum was blocked by an upstream failure (if it
291
+ was definitely blocked, `BLOCKED` is set instead).
284
292
  """
285
293
 
286
- LOGS_MISSING = -2
294
+ ABORTED_SUCCESS = -2
287
295
  """Task metadata was written for this attempt but logs were not.
288
296
 
289
297
  This is a rare condition that requires a hard failure (i.e. the kind that
@@ -292,20 +300,21 @@ class QuantumAttemptStatus(enum.Enum):
292
300
  """
293
301
 
294
302
  FAILED = -1
295
- """Execution of the quantum failed.
303
+ """Execution of the quantum failed gracefully.
296
304
 
297
305
  This is always set if the task metadata dataset was not written but logs
298
306
  were, as is the case when a Python exception is caught and handled by the
299
- execution system. It may also be set in cases where logs were not written
300
- either, but other information was available (e.g. from higher-level
301
- orchestration tooling) to mark it as a failure.
307
+ execution system.
308
+
309
+ This status guarantees that the task log dataset was produced but the
310
+ metadata dataset was not.
302
311
  """
303
312
 
304
313
  BLOCKED = 0
305
314
  """This quantum was not executed because an upstream quantum failed.
306
315
 
307
- Upstream quanta with status `UNKNOWN` or `FAILED` are considered blockers;
308
- `LOGS_MISSING` is not.
316
+ Upstream quanta with status `UNKNOWN`, `FAILED`, or `ABORTED` are
317
+ considered blockers; `ABORTED_SUCCESS` is not.
309
318
  """
310
319
 
311
320
  SUCCESSFUL = 1
@@ -319,6 +328,16 @@ class QuantumAttemptStatus(enum.Enum):
319
328
  these "successes with caveats" are reported.
320
329
  """
321
330
 
331
+ @property
332
+ def has_metadata(self) -> bool:
333
+ """Whether the task metadata dataset was produced."""
334
+ return self is self.SUCCESSFUL or self is self.ABORTED_SUCCESS
335
+
336
+ @property
337
+ def has_log(self) -> bool:
338
+ """Whether the log dataset was produced."""
339
+ return self is self.SUCCESSFUL or self is self.FAILED
340
+
322
341
 
323
342
  class GetSetDictMetadataHolder(Protocol):
324
343
  """Protocol for objects that have a ``metadata`` attribute that satisfies
@@ -26,7 +26,7 @@
26
26
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
27
27
 
28
28
  """Constants used to define the connections automatically added for each
29
- PipelineTask by the execution system.
29
+ PipelineTask by the execution system, as well as other special dataset types.
30
30
  """
31
31
 
32
32
  from __future__ import annotations
@@ -43,6 +43,8 @@ __all__ = (
43
43
  "METADATA_OUTPUT_TEMPLATE",
44
44
  "PACKAGES_INIT_OUTPUT_NAME",
45
45
  "PACKAGES_INIT_OUTPUT_STORAGE_CLASS",
46
+ "PROVENANCE_DATASET_TYPE_NAME",
47
+ "PROVENANCE_STORAGE_CLASS",
46
48
  )
47
49
 
48
50
 
@@ -91,3 +93,9 @@ type names.
91
93
  METADATA_OUTPUT_STORAGE_CLASS: str = "TaskMetadata"
92
94
  """Name of the storage class for task metadata output datasets.
93
95
  """
96
+
97
+ PROVENANCE_DATASET_TYPE_NAME: str = "run_provenance"
98
+ """Name of the dataset used to store per-RUN provenance."""
99
+
100
+ PROVENANCE_STORAGE_CLASS: str = "ProvenanceQuantumGraph"
101
+ """Name of the storage class used to store provenance."""
@@ -25,6 +25,20 @@
25
25
  # You should have received a copy of the GNU General Public License
26
26
  # along with this program. If not, see <https://www.gnu.org/licenses/>.
27
27
 
28
- __all__ = ["register_instrument", "transfer_from_graph", "zip_from_graph", "retrieve_artifacts_for_quanta", "aggregate_graph"]
28
+ __all__ = [
29
+ "register_instrument",
30
+ "transfer_from_graph",
31
+ "zip_from_graph",
32
+ "retrieve_artifacts_for_quanta",
33
+ "aggregate_graph",
34
+ "ingest_graph",
35
+ ]
29
36
 
30
- from .commands import (register_instrument, retrieve_artifacts_for_quanta, transfer_from_graph, zip_from_graph, aggregate_graph)
37
+ from .commands import (
38
+ register_instrument,
39
+ retrieve_artifacts_for_quanta,
40
+ transfer_from_graph,
41
+ zip_from_graph,
42
+ aggregate_graph,
43
+ ingest_graph,
44
+ )
@@ -161,7 +161,7 @@ _AGGREGATOR_DEFAULTS = aggregator.AggregatorConfig()
161
161
 
162
162
  @click.command(short_help="Scan for the outputs of an active or completed quantum graph.", cls=ButlerCommand)
163
163
  @click.argument("predicted_graph", required=True)
164
- @repo_argument(required=True, help="Path to the central butler repository.")
164
+ @repo_argument(required=True, help="Path or alias for the butler repository.")
165
165
  @click.option(
166
166
  "-o",
167
167
  "--output",
@@ -181,9 +181,9 @@ _AGGREGATOR_DEFAULTS = aggregator.AggregatorConfig()
181
181
  help="Number of processes to use.",
182
182
  )
183
183
  @click.option(
184
- "--complete/--incomplete",
185
- "assume_complete",
186
- default=_AGGREGATOR_DEFAULTS.assume_complete,
184
+ "--incomplete/--complete",
185
+ "incomplete",
186
+ default=_AGGREGATOR_DEFAULTS.incomplete,
187
187
  help="Whether execution has completed (and failures cannot be retried).",
188
188
  )
189
189
  @click.option(
@@ -249,6 +249,14 @@ _AGGREGATOR_DEFAULTS = aggregator.AggregatorConfig()
249
249
  default=_AGGREGATOR_DEFAULTS.mock_storage_classes,
250
250
  help="Enable support for storage classes created by the lsst.pipe.base.tests.mocks package.",
251
251
  )
252
+ @click.option(
253
+ "--promise-ingest-graph/--no-promise-ingest-graph",
254
+ default=_AGGREGATOR_DEFAULTS.promise_ingest_graph,
255
+ help=(
256
+ "Promise to run 'butler ingest-graph' later, allowing aggregate-graph "
257
+ "to skip metadata/log/config ingestion for now."
258
+ ),
259
+ )
252
260
  def aggregate_graph(predicted_graph: str, repo: str, **kwargs: Any) -> None:
253
261
  """Scan for quantum graph's outputs to gather provenance, ingest datasets
254
262
  into the central butler repository, and delete datasets that are no
@@ -268,3 +276,33 @@ def aggregate_graph(predicted_graph: str, repo: str, **kwargs: Any) -> None:
268
276
  # When this exception is raised, we'll have already logged the relevant
269
277
  # traceback from a separate worker.
270
278
  raise click.ClickException(str(err)) from None
279
+
280
+
281
+ @click.command(
282
+ short_help="Ingest a provenance quantum graph into a butler, finalizing a RUN collection.",
283
+ cls=ButlerCommand,
284
+ )
285
+ @repo_argument(required=True, help="Path or alias for the butler repository.")
286
+ @click.argument("provenance_graph", required=False)
287
+ @transfer_option(default="move")
288
+ @click.option("--batch-size", default=10000, help="How many datasets to process in each transaction.")
289
+ @click.option(
290
+ "--output-run",
291
+ default=None,
292
+ help=(
293
+ "Name of the output RUN collection. Must be provided if the provenance graph is not"
294
+ " provided (so the graph can be found in the butler)."
295
+ ),
296
+ )
297
+ def ingest_graph(
298
+ *,
299
+ repo: str,
300
+ provenance_graph: str | None,
301
+ transfer: str | None,
302
+ batch_size: int,
303
+ output_run: str | None,
304
+ ) -> None:
305
+ """Ingest a provenance graph into a butler repository."""
306
+ from ...quantum_graph.ingest_graph import ingest_graph as ingest_graph_py
307
+
308
+ ingest_graph_py(repo, provenance_graph, transfer=transfer, batch_size=batch_size, output_run=output_run)