lsst-pipe-base 30.0.0rc3__tar.gz → 30.2025.5100__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 (190) hide show
  1. {lsst_pipe_base-30.0.0rc3/python/lsst_pipe_base.egg-info → lsst_pipe_base-30.2025.5100}/PKG-INFO +1 -1
  2. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/CHANGES.rst +0 -66
  3. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/_instrument.py +5 -6
  4. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/log_capture.py +79 -39
  5. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/mp_graph_executor.py +15 -51
  6. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/_common.py +3 -4
  7. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/_multiblock.py +16 -6
  8. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/_predicted.py +10 -104
  9. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/_provenance.py +6 -657
  10. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/aggregator/_communicators.py +50 -18
  11. lsst_pipe_base-30.2025.5100/python/lsst/pipe/base/quantum_graph/aggregator/_scanner.py +511 -0
  12. lsst_pipe_base-30.2025.5100/python/lsst/pipe/base/quantum_graph/aggregator/_structs.py +177 -0
  13. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/aggregator/_supervisor.py +5 -10
  14. lsst_pipe_base-30.2025.5100/python/lsst/pipe/base/quantum_graph/aggregator/_writer.py +501 -0
  15. lsst_pipe_base-30.2025.5100/python/lsst/pipe/base/quantum_graph_executor.py +126 -0
  16. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/separable_pipeline_executor.py +2 -18
  17. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/single_quantum_executor.py +35 -53
  18. lsst_pipe_base-30.2025.5100/python/lsst/pipe/base/version.py +2 -0
  19. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100/python/lsst_pipe_base.egg-info}/PKG-INFO +1 -1
  20. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst_pipe_base.egg-info/SOURCES.txt +0 -2
  21. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_separable_pipeline_executor.py +13 -101
  22. lsst_pipe_base-30.0.0rc3/python/lsst/pipe/base/log_on_close.py +0 -79
  23. lsst_pipe_base-30.0.0rc3/python/lsst/pipe/base/quantum_graph/aggregator/_scanner.py +0 -317
  24. lsst_pipe_base-30.0.0rc3/python/lsst/pipe/base/quantum_graph/aggregator/_structs.py +0 -67
  25. lsst_pipe_base-30.0.0rc3/python/lsst/pipe/base/quantum_graph/aggregator/_writer.py +0 -184
  26. lsst_pipe_base-30.0.0rc3/python/lsst/pipe/base/quantum_graph/formatter.py +0 -101
  27. lsst_pipe_base-30.0.0rc3/python/lsst/pipe/base/quantum_graph_executor.py +0 -229
  28. lsst_pipe_base-30.0.0rc3/python/lsst/pipe/base/version.py +0 -2
  29. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/COPYRIGHT +0 -0
  30. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/LICENSE +0 -0
  31. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/MANIFEST.in +0 -0
  32. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/README.md +0 -0
  33. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/bsd_license.txt +0 -0
  34. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/creating-a-pipeline.rst +0 -0
  35. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/creating-a-pipelinetask.rst +0 -0
  36. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/creating-a-task.rst +0 -0
  37. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/index.rst +0 -0
  38. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/task-framework-overview.rst +0 -0
  39. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/task-retargeting-howto.rst +0 -0
  40. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/testing-a-pipeline-task.rst +0 -0
  41. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/testing-pipelines-with-mocks.rst +0 -0
  42. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/doc/lsst.pipe.base/working-with-pipeline-graphs.rst +0 -0
  43. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/gpl-v3.0.txt +0 -0
  44. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/pyproject.toml +0 -0
  45. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/__init__.py +0 -0
  46. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/__init__.py +0 -0
  47. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/__init__.py +0 -0
  48. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/_datasetQueryConstraints.py +0 -0
  49. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/_dataset_handle.py +0 -0
  50. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/_observation_dimension_packer.py +0 -0
  51. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/_quantumContext.py +0 -0
  52. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/_status.py +0 -0
  53. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/_task_metadata.py +0 -0
  54. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/all_dimensions_quantum_graph_builder.py +0 -0
  55. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/automatic_connection_constants.py +0 -0
  56. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/caching_limited_butler.py +0 -0
  57. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/cli/__init__.py +0 -0
  58. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/cli/_get_cli_subcommands.py +0 -0
  59. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/cli/cmd/__init__.py +0 -0
  60. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/cli/cmd/commands.py +0 -0
  61. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/cli/opt/__init__.py +0 -0
  62. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/cli/opt/arguments.py +0 -0
  63. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/cli/opt/options.py +0 -0
  64. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/config.py +0 -0
  65. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/configOverrides.py +0 -0
  66. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/connectionTypes.py +0 -0
  67. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/connections.py +0 -0
  68. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/dot_tools.py +0 -0
  69. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/exec_fixup_data_id.py +0 -0
  70. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/execution_graph_fixup.py +0 -0
  71. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/execution_reports.py +0 -0
  72. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/formatters/__init__.py +0 -0
  73. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/formatters/pexConfig.py +0 -0
  74. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/graph/__init__.py +0 -0
  75. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/graph/_implDetails.py +0 -0
  76. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/graph/_loadHelpers.py +0 -0
  77. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/graph/_versionDeserializers.py +0 -0
  78. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/graph/graph.py +0 -0
  79. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/graph/graphSummary.py +0 -0
  80. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/graph/quantumNode.py +0 -0
  81. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/graph_walker.py +0 -0
  82. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/mermaid_tools.py +0 -0
  83. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline.py +0 -0
  84. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipelineIR.py +0 -0
  85. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipelineTask.py +0 -0
  86. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/__init__.py +0 -0
  87. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/__main__.py +0 -0
  88. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/_dataset_types.py +0 -0
  89. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/_edges.py +0 -0
  90. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/_exceptions.py +0 -0
  91. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/_mapping_views.py +0 -0
  92. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/_nodes.py +0 -0
  93. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/_pipeline_graph.py +0 -0
  94. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/_task_subsets.py +0 -0
  95. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/_tasks.py +0 -0
  96. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/expressions.py +0 -0
  97. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/io.py +0 -0
  98. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/__init__.py +0 -0
  99. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_dot.py +0 -0
  100. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py +0 -0
  101. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_layout.py +0 -0
  102. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_merge.py +0 -0
  103. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_mermaid.py +0 -0
  104. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_options.py +0 -0
  105. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_printer.py +0 -0
  106. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_show.py +0 -0
  107. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/pipeline_graph/visualization/_status_annotator.py +0 -0
  108. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/prerequisite_helpers.py +0 -0
  109. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/py.typed +0 -0
  110. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/__init__.py +0 -0
  111. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/aggregator/__init__.py +0 -0
  112. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/aggregator/_config.py +0 -0
  113. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/aggregator/_ingester.py +0 -0
  114. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/aggregator/_progress.py +0 -0
  115. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph/visualization.py +0 -0
  116. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph_builder.py +0 -0
  117. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_graph_skeleton.py +0 -0
  118. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_provenance_graph.py +0 -0
  119. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/quantum_reports.py +0 -0
  120. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/resource_usage.py +0 -0
  121. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/script/__init__.py +0 -0
  122. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/script/register_instrument.py +0 -0
  123. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/script/retrieve_artifacts_for_quanta.py +0 -0
  124. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/script/transfer_from_graph.py +0 -0
  125. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/script/utils.py +0 -0
  126. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/script/zip_from_graph.py +0 -0
  127. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/simple_pipeline_executor.py +0 -0
  128. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/struct.py +0 -0
  129. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/task.py +0 -0
  130. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/taskFactory.py +0 -0
  131. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/testUtils.py +0 -0
  132. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/__init__.py +0 -0
  133. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/in_memory_limited_butler.py +0 -0
  134. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/mocks/__init__.py +0 -0
  135. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/mocks/_data_id_match.py +0 -0
  136. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/mocks/_pipeline_task.py +0 -0
  137. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/mocks/_repo.py +0 -0
  138. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/mocks/_storage_class.py +0 -0
  139. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/no_dimensions.py +0 -0
  140. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/pipelineStepTester.py +0 -0
  141. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/simpleQGraph.py +0 -0
  142. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/tests/util.py +0 -0
  143. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst/pipe/base/utils.py +0 -0
  144. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst_pipe_base.egg-info/dependency_links.txt +0 -0
  145. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst_pipe_base.egg-info/entry_points.txt +0 -0
  146. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst_pipe_base.egg-info/requires.txt +0 -0
  147. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst_pipe_base.egg-info/top_level.txt +0 -0
  148. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/python/lsst_pipe_base.egg-info/zip-safe +0 -0
  149. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/setup.cfg +0 -0
  150. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_adjust_all_quanta.py +0 -0
  151. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_aggregator.py +0 -0
  152. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_caching_limited_butler.py +0 -0
  153. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_cliCmdRegisterInstrument.py +0 -0
  154. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_configOverrides.py +0 -0
  155. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_config_formatter.py +0 -0
  156. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_connections.py +0 -0
  157. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_dataid_match.py +0 -0
  158. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_dataset_handle.py +0 -0
  159. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_deferredDatasetRef.py +0 -0
  160. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_dot_tools.py +0 -0
  161. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_dynamic_connections.py +0 -0
  162. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_execution_reports.py +0 -0
  163. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_execution_storage_class_conversion.py +0 -0
  164. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_graphBuilder.py +0 -0
  165. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_graph_walker.py +0 -0
  166. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_init_output_run.py +0 -0
  167. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_instrument.py +0 -0
  168. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_mermaid.py +0 -0
  169. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_mp_graph_executor.py +0 -0
  170. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_pipeline.py +0 -0
  171. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_pipelineIR.py +0 -0
  172. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_pipelineLoadSubset.py +0 -0
  173. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_pipelineTask.py +0 -0
  174. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_pipeline_graph.py +0 -0
  175. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_pipeline_graph_expressions.py +0 -0
  176. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_predicted_qg.py +0 -0
  177. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_qg_builder_dimensions.py +0 -0
  178. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_quantumGraph.py +0 -0
  179. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_quantum_provenance_graph.py +0 -0
  180. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_quantum_reports.py +0 -0
  181. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_quantum_success_caveats.py +0 -0
  182. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_script_utils.py +0 -0
  183. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_simple_pipeline_executor.py +0 -0
  184. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_single_quantum_executor.py +0 -0
  185. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_struct.py +0 -0
  186. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_task.py +0 -0
  187. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_task_factory.py +0 -0
  188. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_taskmetadata.py +0 -0
  189. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_testUtils.py +0 -0
  190. {lsst_pipe_base-30.0.0rc3 → lsst_pipe_base-30.2025.5100}/tests/test_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lsst-pipe-base
3
- Version: 30.0.0rc3
3
+ Version: 30.2025.5100
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
@@ -1,69 +1,3 @@
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
-
67
1
  lsst-pipe-base v29.1.0 (2025-06-13)
68
2
  ===================================
69
3
 
@@ -31,6 +31,7 @@ __all__ = ("Instrument",)
31
31
 
32
32
  import contextlib
33
33
  import datetime
34
+ import os.path
34
35
  from abc import ABCMeta, abstractmethod
35
36
  from collections.abc import Sequence
36
37
  from typing import TYPE_CHECKING, Any, Self, cast, final
@@ -38,7 +39,6 @@ from typing import TYPE_CHECKING, Any, Self, cast, final
38
39
  from lsst.daf.butler import DataCoordinate, DataId, DimensionPacker, DimensionRecord, Formatter
39
40
  from lsst.daf.butler.registry import DataIdError
40
41
  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[ResourcePathExpression] = ()
68
+ configPaths: Sequence[str] = ()
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
@@ -366,10 +366,9 @@ class Instrument(metaclass=ABCMeta):
366
366
  Config instance to which overrides should be applied.
367
367
  """
368
368
  for root in self.configPaths:
369
- resource = ResourcePath(root, forceDirectory=True, forceAbsolute=True)
370
- uri = resource.join(f"{name}.py", forceDirectory=False)
371
- if uri.exists():
372
- config.load(uri)
369
+ path = os.path.join(root, f"{name}.py")
370
+ if os.path.exists(path):
371
+ config.load(path)
373
372
 
374
373
  @staticmethod
375
374
  def formatCollectionTimestamp(timestamp: str | datetime.datetime) -> str:
@@ -31,15 +31,17 @@ __all__ = ["LogCapture"]
31
31
 
32
32
  import dataclasses
33
33
  import logging
34
+ import os
35
+ import shutil
36
+ import tempfile
34
37
  import uuid
35
38
  from collections.abc import Iterator
36
- from contextlib import contextmanager
39
+ from contextlib import contextmanager, suppress
37
40
  from logging import FileHandler
38
41
 
39
42
  import pydantic
40
43
 
41
- from lsst.daf.butler import Butler, LimitedButler, Quantum
42
- from lsst.daf.butler._rubin.temporary_for_ingest import TemporaryForIngest
44
+ from lsst.daf.butler import Butler, FileDataset, LimitedButler, Quantum
43
45
  from lsst.daf.butler.logging import (
44
46
  ButlerLogRecord,
45
47
  ButlerLogRecordHandler,
@@ -163,9 +165,7 @@ class LogCapture:
163
165
  return cls(butler, butler)
164
166
 
165
167
  @contextmanager
166
- def capture_logging(
167
- self, task_node: TaskNode, /, quantum: Quantum, records: ButlerLogRecords | None = None
168
- ) -> Iterator[_LogCaptureContext]:
168
+ def capture_logging(self, task_node: TaskNode, /, quantum: Quantum) -> Iterator[_LogCaptureContext]:
169
169
  """Configure logging system to capture logs for execution of this task.
170
170
 
171
171
  Parameters
@@ -174,9 +174,6 @@ class LogCapture:
174
174
  The task definition.
175
175
  quantum : `~lsst.daf.butler.Quantum`
176
176
  Single Quantum instance.
177
- records : `lsst.daf.butler.logging.ButlerLogRecords`, optional
178
- Log record container to append to and save. If provided, streaming
179
- mode is disabled (since we'll be saving logs in memory anyway).
180
177
 
181
178
  Notes
182
179
  -----
@@ -208,40 +205,44 @@ class LogCapture:
208
205
 
209
206
  # Add a handler to the root logger to capture execution log output.
210
207
  if log_dataset_name is not None:
211
- try:
212
- [ref] = quantum.outputs[log_dataset_name]
213
- except LookupError as exc:
214
- raise InvalidQuantumError(
215
- f"Quantum outputs is missing log output dataset type {log_dataset_name};"
216
- " this could happen due to inconsistent options between QuantumGraph generation"
217
- " and execution"
218
- ) from exc
219
208
  # Either accumulate into ButlerLogRecords or stream JSON records to
220
209
  # file and ingest that (ingest is possible only with full butler).
221
- if self.stream_json_logs and self.full_butler is not None and records is None:
222
- with TemporaryForIngest(self.full_butler, ref) as temporary:
223
- log_handler_file = FileHandler(temporary.ospath)
224
- log_handler_file.setFormatter(JsonLogFormatter())
225
- logging.getLogger().addHandler(log_handler_file)
226
-
227
- try:
228
- with ButlerMDC.set_mdc(mdc):
229
- yield ctx
230
- finally:
231
- # Ensure that the logs are stored in butler.
232
- logging.getLogger().removeHandler(log_handler_file)
233
- log_handler_file.close()
234
- if ctx.extra:
235
- with open(temporary.ospath, "a") as log_stream:
236
- ButlerLogRecords.write_streaming_extra(
237
- log_stream,
238
- ctx.extra.model_dump_json(exclude_unset=True, exclude_defaults=True),
239
- )
240
- if ctx.store:
241
- temporary.ingest()
210
+ if self.stream_json_logs and self.full_butler is not None:
211
+ # Create the log file in a temporary directory rather than
212
+ # creating a temporary file. This is necessary because
213
+ # temporary files are created with restrictive permissions
214
+ # and during file ingest these permissions persist in the
215
+ # datastore. Using a temp directory allows us to create
216
+ # a file with umask default permissions.
217
+ tmpdir = tempfile.mkdtemp(prefix="butler-temp-logs-")
218
+
219
+ # Construct a file to receive the log records and "touch" it.
220
+ log_file = os.path.join(tmpdir, f"butler-log-{task_node.label}.json")
221
+ with open(log_file, "w"):
222
+ pass
223
+ log_handler_file = FileHandler(log_file)
224
+ log_handler_file.setFormatter(JsonLogFormatter())
225
+ logging.getLogger().addHandler(log_handler_file)
226
+
227
+ try:
228
+ with ButlerMDC.set_mdc(mdc):
229
+ yield ctx
230
+ finally:
231
+ # Ensure that the logs are stored in butler.
232
+ logging.getLogger().removeHandler(log_handler_file)
233
+ log_handler_file.close()
234
+ if ctx.extra:
235
+ with open(log_file, "a") as log_stream:
236
+ ButlerLogRecords.write_streaming_extra(
237
+ log_stream,
238
+ ctx.extra.model_dump_json(exclude_unset=True, exclude_defaults=True),
239
+ )
240
+ if ctx.store:
241
+ self._ingest_log_records(quantum, log_dataset_name, log_file)
242
+ shutil.rmtree(tmpdir, ignore_errors=True)
242
243
 
243
244
  else:
244
- log_handler_memory = ButlerLogRecordHandler(records)
245
+ log_handler_memory = ButlerLogRecordHandler()
245
246
  logging.getLogger().addHandler(log_handler_memory)
246
247
 
247
248
  try:
@@ -260,6 +261,7 @@ class LogCapture:
260
261
  logging.getLogger().removeHandler(log_handler_memory)
261
262
  if ctx.store:
262
263
  self._store_log_records(quantum, log_dataset_name, log_handler_memory)
264
+ log_handler_memory.records.clear()
263
265
 
264
266
  else:
265
267
  with ButlerMDC.set_mdc(mdc):
@@ -279,3 +281,41 @@ class LogCapture:
279
281
  ) from exc
280
282
 
281
283
  self.butler.put(log_handler.records, ref)
284
+
285
+ def _ingest_log_records(self, quantum: Quantum, dataset_type: str, filename: str) -> None:
286
+ # If we are logging to an external file we must always try to
287
+ # close it.
288
+ assert self.full_butler is not None, "Expected to have full butler for ingest"
289
+ ingested = False
290
+ try:
291
+ # DatasetRef has to be in the Quantum outputs, can lookup by name.
292
+ try:
293
+ [ref] = quantum.outputs[dataset_type]
294
+ except LookupError as exc:
295
+ raise InvalidQuantumError(
296
+ f"Quantum outputs is missing log output dataset type {dataset_type};"
297
+ " this could happen due to inconsistent options between QuantumGraph generation"
298
+ " and execution"
299
+ ) from exc
300
+
301
+ # Need to ingest this file directly into butler.
302
+ dataset = FileDataset(path=filename, refs=ref)
303
+ try:
304
+ self.full_butler.ingest(dataset, transfer="move")
305
+ ingested = True
306
+ except NotImplementedError:
307
+ # Some datastores can't receive files (e.g. in-memory datastore
308
+ # when testing), we store empty list for those just to have a
309
+ # dataset. Alternative is to read the file as a
310
+ # ButlerLogRecords object and put it.
311
+ _LOG.info(
312
+ "Log records could not be stored in this butler because the"
313
+ " datastore can not ingest files, empty record list is stored instead."
314
+ )
315
+ records = ButlerLogRecords.from_records([])
316
+ self.full_butler.put(records, ref)
317
+ finally:
318
+ # remove file if it is not ingested
319
+ if not ingested:
320
+ with suppress(OSError):
321
+ os.remove(filename)
@@ -39,24 +39,20 @@ import sys
39
39
  import threading
40
40
  import time
41
41
  import uuid
42
- from contextlib import ExitStack
43
42
  from typing import Literal, cast
44
43
 
45
44
  import networkx
46
45
 
47
46
  from lsst.daf.butler import DataCoordinate, Quantum
48
47
  from lsst.daf.butler.cli.cliLog import CliLog
49
- from lsst.daf.butler.logging import ButlerLogRecords
50
48
  from lsst.utils.threads import disable_implicit_threading
51
49
 
52
50
  from ._status import InvalidQuantumError, RepeatableQuantumError
53
- from ._task_metadata import TaskMetadata
54
51
  from .execution_graph_fixup import ExecutionGraphFixup
55
52
  from .graph import QuantumGraph
56
53
  from .graph_walker import GraphWalker
57
- from .log_on_close import LogOnClose
58
54
  from .pipeline_graph import TaskNode
59
- from .quantum_graph import PredictedQuantumGraph, PredictedQuantumInfo, ProvenanceQuantumGraphWriter
55
+ from .quantum_graph import PredictedQuantumGraph, PredictedQuantumInfo
60
56
  from .quantum_graph_executor import QuantumExecutor, QuantumGraphExecutor
61
57
  from .quantum_reports import ExecutionStatus, QuantumReport, Report
62
58
 
@@ -519,9 +515,7 @@ class MPGraphExecutor(QuantumGraphExecutor):
519
515
  start_method = "spawn"
520
516
  self._start_method = start_method
521
517
 
522
- def execute(
523
- self, graph: QuantumGraph | PredictedQuantumGraph, *, provenance_graph_file: str | None = None
524
- ) -> None:
518
+ def execute(self, graph: QuantumGraph | PredictedQuantumGraph) -> None:
525
519
  # Docstring inherited from QuantumGraphExecutor.execute
526
520
  old_graph: QuantumGraph | None = None
527
521
  if isinstance(graph, QuantumGraph):
@@ -531,31 +525,14 @@ class MPGraphExecutor(QuantumGraphExecutor):
531
525
  new_graph = graph
532
526
  xgraph = self._make_xgraph(new_graph, old_graph)
533
527
  self._report = Report(qgraphSummary=new_graph._make_summary())
534
- with ExitStack() as exit_stack:
535
- provenance_writer: ProvenanceQuantumGraphWriter | None = None
536
- if provenance_graph_file is not None:
537
- if provenance_graph_file is not None and self._num_proc > 1:
538
- raise NotImplementedError(
539
- "Provenance writing is not implemented for multiprocess execution."
540
- )
541
- provenance_writer = ProvenanceQuantumGraphWriter(
542
- provenance_graph_file,
543
- exit_stack=exit_stack,
544
- log_on_close=LogOnClose(_LOG.log),
545
- predicted=new_graph,
546
- )
547
- try:
548
- if self._num_proc > 1:
549
- self._execute_quanta_mp(xgraph, self._report)
550
- else:
551
- self._execute_quanta_in_process(xgraph, self._report, provenance_writer)
552
- except Exception as exc:
553
- self._report.set_exception(exc)
554
- raise
555
- if provenance_writer is not None:
556
- provenance_writer.write_overall_inputs()
557
- provenance_writer.write_packages()
558
- provenance_writer.write_init_outputs(assume_existence=True)
528
+ try:
529
+ if self._num_proc > 1:
530
+ self._execute_quanta_mp(xgraph, self._report)
531
+ else:
532
+ self._execute_quanta_in_process(xgraph, self._report)
533
+ except Exception as exc:
534
+ self._report.set_exception(exc)
535
+ raise
559
536
 
560
537
  def _make_xgraph(
561
538
  self, new_graph: PredictedQuantumGraph, old_graph: QuantumGraph | None
@@ -599,9 +576,7 @@ class MPGraphExecutor(QuantumGraphExecutor):
599
576
  raise MPGraphExecutorError("Updated execution graph has dependency cycle.")
600
577
  return xgraph
601
578
 
602
- def _execute_quanta_in_process(
603
- self, xgraph: networkx.DiGraph, report: Report, provenance_writer: ProvenanceQuantumGraphWriter | None
604
- ) -> None:
579
+ def _execute_quanta_in_process(self, xgraph: networkx.DiGraph, report: Report) -> None:
605
580
  """Execute all Quanta in current process.
606
581
 
607
582
  Parameters
@@ -614,9 +589,6 @@ class MPGraphExecutor(QuantumGraphExecutor):
614
589
  `.quantum_graph.PredictedQuantumGraph.quantum_only_xgraph`.
615
590
  report : `Report`
616
591
  Object for reporting execution status.
617
- provenance_writer : `.quantum_graph.ProvenanceQuantumGraphWriter` or \
618
- `None`
619
- Object for recording provenance.
620
592
  """
621
593
 
622
594
  def tiebreaker_sort_key(quantum_id: uuid.UUID) -> tuple:
@@ -634,19 +606,16 @@ class MPGraphExecutor(QuantumGraphExecutor):
634
606
 
635
607
  _LOG.debug("Executing %s (%s@%s)", quantum_id, task_node.label, data_id)
636
608
  fail_exit_code: int | None = None
637
- task_metadata: TaskMetadata | None = None
638
- task_logs = ButlerLogRecords([])
639
609
  try:
640
610
  # For some exception types we want to exit immediately with
641
611
  # exception-specific exit code, but we still want to start
642
612
  # debugger before exiting if debugging is enabled.
643
613
  try:
644
- execution_result = self._quantum_executor.execute(
645
- task_node, quantum, quantum_id=quantum_id, log_records=task_logs
614
+ _, quantum_report = self._quantum_executor.execute(
615
+ task_node, quantum, quantum_id=quantum_id
646
616
  )
647
- if execution_result.report:
648
- report.quantaReports.append(execution_result.report)
649
- task_metadata = execution_result.task_metadata
617
+ if quantum_report:
618
+ report.quantaReports.append(quantum_report)
650
619
  success_count += 1
651
620
  walker.finish(quantum_id)
652
621
  except RepeatableQuantumError as exc:
@@ -732,11 +701,6 @@ class MPGraphExecutor(QuantumGraphExecutor):
732
701
  )
733
702
  failed_count += 1
734
703
 
735
- if provenance_writer is not None:
736
- provenance_writer.write_quantum_provenance(
737
- quantum_id, metadata=task_metadata, logs=task_logs
738
- )
739
-
740
704
  _LOG.info(
741
705
  "Executed %d quanta successfully, %d failed and %d remain out of total %d quanta.",
742
706
  success_count,
@@ -448,15 +448,14 @@ class BaseQuantumGraphWriter:
448
448
  uri: ResourcePathExpression,
449
449
  header: HeaderModel,
450
450
  pipeline_graph: PipelineGraph,
451
+ indices: dict[uuid.UUID, int],
451
452
  *,
452
453
  address_filename: str,
454
+ compressor: Compressor,
453
455
  cdict_data: bytes | None = None,
454
- zstd_level: int = 10,
455
456
  ) -> Iterator[Self]:
456
457
  uri = ResourcePath(uri)
457
- address_writer = AddressWriter()
458
- cdict = zstandard.ZstdCompressionDict(cdict_data) if cdict_data is not None else None
459
- compressor = zstandard.ZstdCompressor(level=zstd_level, dict_data=cdict)
458
+ address_writer = AddressWriter(indices)
460
459
  with uri.open(mode="wb") as stream:
461
460
  with zipfile.ZipFile(stream, mode="w", compression=zipfile.ZIP_STORED) as zf:
462
461
  self = cls(zf, compressor, address_writer, header.int_size)
@@ -205,6 +205,13 @@ class AddressRow:
205
205
  class AddressWriter:
206
206
  """A helper object for writing address files for multi-block files."""
207
207
 
208
+ indices: dict[uuid.UUID, int] = dataclasses.field(default_factory=dict)
209
+ """Mapping from UUID to internal integer ID.
210
+
211
+ The internal integer ID must always correspond to the index into the
212
+ sorted list of all UUIDs, but this `dict` need not be sorted itself.
213
+ """
214
+
208
215
  addresses: list[dict[uuid.UUID, Address]] = dataclasses.field(default_factory=list)
209
216
  """Addresses to store with each UUID.
210
217
 
@@ -222,15 +229,18 @@ class AddressWriter:
222
229
  int_size : `int`
223
230
  Number of bytes to use for all integers.
224
231
  """
225
- indices: set[uuid.UUID] = set()
226
- for address_map in self.addresses:
227
- indices.update(address_map.keys())
232
+ for n, address_map in enumerate(self.addresses):
233
+ if not self.indices.keys() >= address_map.keys():
234
+ raise AssertionError(
235
+ f"Logic bug in quantum graph I/O: address map {n} of {len(self.addresses)} has IDs "
236
+ f"{address_map.keys() - self.indices.keys()} not in the index map."
237
+ )
228
238
  stream.write(int_size.to_bytes(1))
229
- stream.write(len(indices).to_bytes(int_size))
239
+ stream.write(len(self.indices).to_bytes(int_size))
230
240
  stream.write(len(self.addresses).to_bytes(int_size))
231
241
  empty_address = Address()
232
- for n, key in enumerate(sorted(indices, key=attrgetter("int"))):
233
- row = AddressRow(key, n, [m.get(key, empty_address) for m in self.addresses])
242
+ for key in sorted(self.indices.keys(), key=attrgetter("int")):
243
+ row = AddressRow(key, self.indices[key], [m.get(key, empty_address) for m in self.addresses])
234
244
  _LOG.debug("Wrote address %s.", row)
235
245
  row.write(stream, int_size)
236
246
 
@@ -66,7 +66,6 @@ from lsst.daf.butler import (
66
66
  DimensionDataExtractor,
67
67
  DimensionGroup,
68
68
  DimensionRecordSetDeserializer,
69
- DimensionUniverse,
70
69
  LimitedButler,
71
70
  Quantum,
72
71
  QuantumBackedButler,
@@ -878,49 +877,6 @@ class PredictedQuantumGraph(BaseQuantumGraph):
878
877
  page_size=page_size,
879
878
  ).assemble()
880
879
 
881
- @classmethod
882
- def make_empty(
883
- cls,
884
- universe: DimensionUniverse,
885
- *,
886
- output_run: str,
887
- inputs: Iterable[str] = (),
888
- output: str | None = None,
889
- add_packages: bool = True,
890
- ) -> PredictedQuantumGraph:
891
- """Make an empty quantum graph with no tasks.
892
-
893
- Parameters
894
- ----------
895
- universe : `lsst.daf.butler.DimensionUniverse`
896
- Definitions for all butler dimensions.
897
- output_run : `str`
898
- Output run collection.
899
- inputs : `~collections.abc.Iterable` [`str`], optional
900
- Iterable of input collection names.
901
- output : `str` or `None`, optional
902
- Output chained collection.
903
- add_packages : `bool`, optional
904
- Whether to add the special init quantum that writes the 'packages'
905
- dataset. The default (`True`) is consistent with
906
- `~..quantum_graph_builder.QuantumGraphBuilder` behavior when there
907
- are no regular quanta generated.
908
-
909
- Returns
910
- -------
911
- quantum_graph : `PredictedQuantumGraph`
912
- An empty quantum graph.
913
- """
914
- return cls(
915
- PredictedQuantumGraphComponents.make_empty(
916
- universe,
917
- output_run=output_run,
918
- inputs=inputs,
919
- output=output,
920
- add_packages=add_packages,
921
- )
922
- )
923
-
924
880
  @property
925
881
  def quanta_by_task(self) -> Mapping[str, Mapping[DataCoordinate, uuid.UUID]]:
926
882
  """A nested mapping of all quanta, keyed first by task name and then by
@@ -1585,63 +1541,6 @@ class PredictedQuantumGraphComponents:
1585
1541
  This does not include special "init" quanta.
1586
1542
  """
1587
1543
 
1588
- @classmethod
1589
- def make_empty(
1590
- cls,
1591
- universe: DimensionUniverse,
1592
- *,
1593
- output_run: str,
1594
- inputs: Iterable[str] = (),
1595
- output: str | None = None,
1596
- add_packages: bool = True,
1597
- ) -> PredictedQuantumGraphComponents:
1598
- """Make components for an empty quantum graph with no tasks.
1599
-
1600
- Parameters
1601
- ----------
1602
- universe : `lsst.daf.butler.DimensionUniverse`
1603
- Definitions for all butler dimensions.
1604
- output_run : `str`
1605
- Output run collection.
1606
- inputs : `~collections.abc.Iterable` [`str`], optional
1607
- Iterable of input collection names.
1608
- output : `str` or `None`, optional
1609
- Output chained collection.
1610
- add_packages : `bool`, optional
1611
- Whether to add the special init quantum that writes the 'packages'
1612
- dataset. The default (`True`) is consistent with
1613
- `~..quantum_graph_builder.QuantumGraphBuilder` behavior when there
1614
- are no regular quanta generated.
1615
-
1616
- Returns
1617
- -------
1618
- components : `PredictedQuantumGraphComponents`
1619
- Components that can be used to build or write an empty quantum
1620
- graph.
1621
- """
1622
- components = cls(pipeline_graph=PipelineGraph(universe=universe))
1623
- components.header.inputs = list(inputs)
1624
- components.header.output_run = output_run
1625
- components.header.output = output
1626
- if add_packages:
1627
- components.init_quanta.root = [
1628
- PredictedQuantumDatasetsModel.model_construct(
1629
- quantum_id=generate_uuidv7(),
1630
- task_label="",
1631
- outputs={
1632
- acc.PACKAGES_INIT_OUTPUT_NAME: [
1633
- PredictedDatasetModel(
1634
- dataset_id=generate_uuidv7(),
1635
- dataset_type_name=acc.PACKAGES_INIT_OUTPUT_NAME,
1636
- data_coordinate=[],
1637
- run=output_run,
1638
- )
1639
- ]
1640
- },
1641
- )
1642
- ]
1643
- return components
1644
-
1645
1544
  def make_dataset_ref(self, predicted: PredictedDatasetModel) -> DatasetRef:
1646
1545
  """Make a `lsst.daf.butler.DatasetRef` from information in the
1647
1546
  predicted quantum graph.
@@ -1894,6 +1793,7 @@ class PredictedQuantumGraphComponents:
1894
1793
  f"Unsupported extension {ext!r} for quantum graph; "
1895
1794
  "expected '.qg' (or '.qgraph' to force the old format)."
1896
1795
  )
1796
+ cdict: zstandard.ZstdCompressionDict | None = None
1897
1797
  cdict_data: bytes | None = None
1898
1798
  quantum_datasets_json: dict[uuid.UUID, bytes] = {}
1899
1799
  if len(self.quantum_datasets) < zstd_dict_n_inputs:
@@ -1907,20 +1807,26 @@ class PredictedQuantumGraphComponents:
1907
1807
  for quantum_model in itertools.islice(self.quantum_datasets.values(), zstd_dict_n_inputs)
1908
1808
  }
1909
1809
  try:
1910
- cdict_data = zstandard.train_dictionary(
1810
+ cdict = zstandard.train_dictionary(
1911
1811
  zstd_dict_size,
1912
1812
  list(quantum_datasets_json.values()),
1913
1813
  level=zstd_level,
1914
- ).as_bytes()
1814
+ )
1915
1815
  except zstandard.ZstdError as err:
1916
1816
  warnings.warn(f"Not using a compression dictionary: {err}.")
1817
+ cdict = None
1818
+ else:
1819
+ cdict_data = cdict.as_bytes()
1820
+ compressor = zstandard.ZstdCompressor(level=zstd_level, dict_data=cdict)
1821
+ indices = {quantum_id: n for n, quantum_id in enumerate(sorted(self.quantum_datasets.keys()))}
1917
1822
  with BaseQuantumGraphWriter.open(
1918
1823
  uri,
1919
1824
  header=self.header,
1920
1825
  pipeline_graph=self.pipeline_graph,
1826
+ indices=indices,
1921
1827
  address_filename="quanta",
1828
+ compressor=compressor,
1922
1829
  cdict_data=cdict_data,
1923
- zstd_level=zstd_level,
1924
1830
  ) as writer:
1925
1831
  writer.write_single_model("thin_graph", self.thin_graph)
1926
1832
  if self.dimension_data is None: