lsst-pipe-base 29.2025.3900__tar.gz → 29.2025.4100__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {lsst_pipe_base-29.2025.3900/python/lsst_pipe_base.egg-info → lsst_pipe_base-29.2025.4100}/PKG-INFO +2 -1
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/index.rst +4 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/pyproject.toml +2 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_task_metadata.py +15 -0
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/dot_tools.py +130 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/exec_fixup_data_id.py +17 -44
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/execution_graph_fixup.py +100 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/_versionDeserializers.py +6 -5
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/graph.py +30 -10
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/graphSummary.py +30 -0
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/graph_walker.py +119 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/log_capture.py +5 -2
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/mermaid_tools.py +11 -64
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/mp_graph_executor.py +298 -236
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/io.py +1 -1
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/quantum_graph/__init__.py +32 -0
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/quantum_graph/_common.py +632 -0
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/quantum_graph/_multiblock.py +808 -0
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/quantum_graph/_predicted.py +1950 -0
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/quantum_graph/visualization.py +302 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph_builder.py +292 -34
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph_executor.py +2 -1
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_provenance_graph.py +16 -7
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_reports.py +45 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/separable_pipeline_executor.py +126 -15
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/simple_pipeline_executor.py +44 -43
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/single_quantum_executor.py +1 -40
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/__init__.py +1 -1
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/_pipeline_task.py +16 -1
- lsst_pipe_base-29.2025.3900/python/lsst/pipe/base/tests/mocks/_in_memory_repo.py → lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/tests/mocks/_repo.py +324 -45
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/_storage_class.py +51 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/simpleQGraph.py +11 -5
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/version.py +2 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100/python/lsst_pipe_base.egg-info}/PKG-INFO +2 -1
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/SOURCES.txt +9 -1
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/requires.txt +1 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_adjust_all_quanta.py +5 -2
- lsst_pipe_base-29.2025.4100/tests/test_graph_walker.py +142 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_init_output_run.py +15 -19
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_instrument.py +5 -11
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_mp_graph_executor.py +51 -30
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_pipeline_graph.py +5 -0
- lsst_pipe_base-29.2025.4100/tests/test_predicted_qg.py +607 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_qg_builder_dimensions.py +20 -20
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_separable_pipeline_executor.py +465 -25
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_single_quantum_executor.py +0 -2
- lsst_pipe_base-29.2025.3900/python/lsst/pipe/base/dot_tools.py +0 -268
- lsst_pipe_base-29.2025.3900/python/lsst/pipe/base/execution_graph_fixup.py +0 -69
- lsst_pipe_base-29.2025.3900/python/lsst/pipe/base/version.py +0 -2
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/COPYRIGHT +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/LICENSE +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/MANIFEST.in +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/README.md +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/bsd_license.txt +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/CHANGES.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/creating-a-pipeline.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/creating-a-pipelinetask.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/creating-a-task.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/task-framework-overview.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/task-retargeting-howto.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/testing-a-pipeline-task.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/testing-pipelines-with-mocks.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/working-with-pipeline-graphs.rst +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/gpl-v3.0.txt +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_datasetQueryConstraints.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_dataset_handle.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_instrument.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_observation_dimension_packer.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_quantumContext.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_status.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/all_dimensions_quantum_graph_builder.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/automatic_connection_constants.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/caching_limited_butler.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/_get_cli_subcommands.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/cmd/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/cmd/commands.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/opt/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/opt/arguments.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/opt/options.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/config.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/configOverrides.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/connectionTypes.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/connections.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/execution_reports.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/formatters/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/formatters/pexConfig.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/_implDetails.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/_loadHelpers.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/quantumNode.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipelineIR.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipelineTask.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/__main__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_dataset_types.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_edges.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_exceptions.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_mapping_views.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_nodes.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_pipeline_graph.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_task_subsets.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_tasks.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/expressions.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_dot.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_layout.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_merge.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_mermaid.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_options.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_printer.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_show.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_status_annotator.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/prerequisite_helpers.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/py.typed +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph_skeleton.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/register_instrument.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/retrieve_artifacts_for_quanta.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/transfer_from_graph.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/utils.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/zip_from_graph.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/struct.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/task.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/taskFactory.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/testUtils.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/__init__.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/in_memory_limited_butler.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/_data_id_match.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/no_dimensions.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/pipelineStepTester.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/util.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/utils.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/dependency_links.txt +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/entry_points.txt +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/top_level.txt +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/zip-safe +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/setup.cfg +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_caching_limited_butler.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_cliCmdRegisterInstrument.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_configOverrides.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_config_formatter.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_connections.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_dataid_match.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_dataset_handle.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_dot_tools.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_dynamic_connections.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_execution_reports.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_execution_storage_class_conversion.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_graphBuilder.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_mermaid.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_pipeline.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_pipelineIR.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_pipelineLoadSubset.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_pipelineTask.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_pipeline_graph_expressions.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_quantumGraph.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_quantum_provenance_graph.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_quantum_reports.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_quantum_success_caveats.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_script_utils.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_simple_pipeline_executor.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_struct.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_task.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_task_factory.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_taskmetadata.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_testUtils.py +0 -0
- {lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/tests/test_utils.py +0 -0
{lsst_pipe_base-29.2025.3900/python/lsst_pipe_base.egg-info → lsst_pipe_base-29.2025.4100}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lsst-pipe-base
|
|
3
|
-
Version: 29.2025.
|
|
3
|
+
Version: 29.2025.4100
|
|
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: BSD 3-Clause License
|
|
@@ -31,6 +31,7 @@ Requires-Dist: wcwidth
|
|
|
31
31
|
Requires-Dist: pyyaml>=5.1
|
|
32
32
|
Requires-Dist: numpy>=1.17
|
|
33
33
|
Requires-Dist: frozendict
|
|
34
|
+
Requires-Dist: zstandard<0.24,>=0.23.0
|
|
34
35
|
Provides-Extra: test
|
|
35
36
|
Requires-Dist: pytest>=3.2; extra == "test"
|
|
36
37
|
Provides-Extra: mermaid
|
|
@@ -100,6 +100,10 @@ Python API reference
|
|
|
100
100
|
|
|
101
101
|
.. automodapi:: lsst.pipe.base.separable_pipeline_executor
|
|
102
102
|
|
|
103
|
+
.. automodapi:: lsst.pipe.base.quantum_graph
|
|
104
|
+
|
|
105
|
+
.. automodapi:: lsst.pipe.base.quantum_graph.visualization
|
|
106
|
+
|
|
103
107
|
QuantumGraph generation API reference
|
|
104
108
|
-------------------------------------
|
|
105
109
|
|
|
@@ -34,6 +34,7 @@ dependencies = [
|
|
|
34
34
|
"pyyaml >= 5.1",
|
|
35
35
|
"numpy >= 1.17",
|
|
36
36
|
"frozendict",
|
|
37
|
+
"zstandard >=0.23.0,<0.24",
|
|
37
38
|
]
|
|
38
39
|
|
|
39
40
|
dynamic = ["version"]
|
|
@@ -122,6 +123,7 @@ exclude_lines = [
|
|
|
122
123
|
"raise NotImplementedError",
|
|
123
124
|
"if __name__ == .__main__.:",
|
|
124
125
|
"if TYPE_CHECKING:",
|
|
126
|
+
'if "sphinx" in sys.modules:',
|
|
125
127
|
]
|
|
126
128
|
|
|
127
129
|
[tool.pydocstyle]
|
{lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_task_metadata.py
RENAMED
|
@@ -686,6 +686,21 @@ class TaskMetadata(BaseModel):
|
|
|
686
686
|
"""See `pydantic.BaseModel.model_json_schema`."""
|
|
687
687
|
return super().model_json_schema(*args, **kwargs)
|
|
688
688
|
|
|
689
|
+
@classmethod
|
|
690
|
+
def model_validate(cls, *args: Any, **kwargs: Any) -> Any:
|
|
691
|
+
"""See `pydantic.BaseModel.model_validate`."""
|
|
692
|
+
return super().model_validate(*args, **kwargs)
|
|
693
|
+
|
|
694
|
+
@classmethod
|
|
695
|
+
def model_validate_json(cls, *args: Any, **kwargs: Any) -> Any:
|
|
696
|
+
"""See `pydantic.BaseModel.model_validate_json`."""
|
|
697
|
+
return super().model_validate_json(*args, **kwargs)
|
|
698
|
+
|
|
699
|
+
@classmethod
|
|
700
|
+
def model_validate_strings(cls, *args: Any, **kwargs: Any) -> Any:
|
|
701
|
+
"""See `pydantic.BaseModel.model_validate_strings`."""
|
|
702
|
+
return super().model_validate_strings(*args, **kwargs)
|
|
703
|
+
|
|
689
704
|
|
|
690
705
|
# Needed because a TaskMetadata can contain a TaskMetadata.
|
|
691
706
|
TaskMetadata.model_rebuild()
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# This file is part of pipe_base.
|
|
2
|
+
#
|
|
3
|
+
# Developed for the LSST Data Management System.
|
|
4
|
+
# This product includes software developed by the LSST Project
|
|
5
|
+
# (http://www.lsst.org).
|
|
6
|
+
# See the COPYRIGHT file at the top-level directory of this distribution
|
|
7
|
+
# for details of code ownership.
|
|
8
|
+
#
|
|
9
|
+
# This software is dual licensed under the GNU General Public License and also
|
|
10
|
+
# under a 3-clause BSD license. Recipients may choose which of these licenses
|
|
11
|
+
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
|
|
12
|
+
# respectively. If you choose the GPL option then the following text applies
|
|
13
|
+
# (but note that there is still no warranty even if you opt for BSD instead):
|
|
14
|
+
#
|
|
15
|
+
# This program is free software: you can redistribute it and/or modify
|
|
16
|
+
# it under the terms of the GNU General Public License as published by
|
|
17
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
18
|
+
# (at your option) any later version.
|
|
19
|
+
#
|
|
20
|
+
# This program is distributed in the hope that it will be useful,
|
|
21
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
22
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
23
|
+
# GNU General Public License for more details.
|
|
24
|
+
#
|
|
25
|
+
# You should have received a copy of the GNU General Public License
|
|
26
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
27
|
+
|
|
28
|
+
"""Module defining few methods to generate GraphViz diagrams from pipelines
|
|
29
|
+
or quantum graphs.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
__all__ = ["graph2dot", "pipeline2dot"]
|
|
35
|
+
|
|
36
|
+
from collections.abc import Iterable
|
|
37
|
+
from typing import TYPE_CHECKING, Any
|
|
38
|
+
|
|
39
|
+
from .pipeline import Pipeline
|
|
40
|
+
|
|
41
|
+
if TYPE_CHECKING:
|
|
42
|
+
from .graph import QuantumGraph
|
|
43
|
+
from .pipeline import TaskDef
|
|
44
|
+
from .quantum_graph import PredictedQuantumGraph
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def graph2dot(qgraph: QuantumGraph | PredictedQuantumGraph, file: Any) -> None:
|
|
48
|
+
"""Convert QuantumGraph into GraphViz digraph.
|
|
49
|
+
|
|
50
|
+
This method is mostly for documentation/presentation purposes.
|
|
51
|
+
|
|
52
|
+
Parameters
|
|
53
|
+
----------
|
|
54
|
+
qgraph : `lsst.pipe.base.QuantumGraph` or \
|
|
55
|
+
`lsst.pipe.base.quantum_graph.PredictedQuantumGraph`
|
|
56
|
+
Quantum graph object.
|
|
57
|
+
file : `str` or file object
|
|
58
|
+
File where GraphViz graph (DOT language) is written, can be a file name
|
|
59
|
+
or file object.
|
|
60
|
+
|
|
61
|
+
Raises
|
|
62
|
+
------
|
|
63
|
+
OSError
|
|
64
|
+
Raised if the output file cannot be opened.
|
|
65
|
+
ImportError
|
|
66
|
+
Raised if the task class cannot be imported.
|
|
67
|
+
"""
|
|
68
|
+
from .quantum_graph import PredictedQuantumGraph, visualization
|
|
69
|
+
|
|
70
|
+
if not isinstance(qgraph, PredictedQuantumGraph):
|
|
71
|
+
qgraph = PredictedQuantumGraph.from_old_quantum_graph(qgraph)
|
|
72
|
+
|
|
73
|
+
# open a file if needed
|
|
74
|
+
close = False
|
|
75
|
+
if not hasattr(file, "write"):
|
|
76
|
+
file = open(file, "w")
|
|
77
|
+
close = True
|
|
78
|
+
|
|
79
|
+
v = visualization.QuantumGraphDotVisualizer()
|
|
80
|
+
v.write_bipartite(qgraph, file)
|
|
81
|
+
|
|
82
|
+
if close:
|
|
83
|
+
file.close()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def pipeline2dot(pipeline: Pipeline | Iterable[TaskDef], file: Any) -> None:
|
|
87
|
+
"""Convert `~lsst.pipe.base.Pipeline` into GraphViz digraph.
|
|
88
|
+
|
|
89
|
+
This method is mostly for documentation/presentation purposes.
|
|
90
|
+
Unlike other methods this method does not validate graph consistency.
|
|
91
|
+
|
|
92
|
+
Parameters
|
|
93
|
+
----------
|
|
94
|
+
pipeline : `.Pipeline` or `~collections.abc.Iterable` [ `.TaskDef` ]
|
|
95
|
+
Pipeline description.
|
|
96
|
+
file : `str` or file object
|
|
97
|
+
File where GraphViz graph (DOT language) is written, can be a file name
|
|
98
|
+
or file object.
|
|
99
|
+
|
|
100
|
+
Raises
|
|
101
|
+
------
|
|
102
|
+
OSError
|
|
103
|
+
Raised if the output file cannot be opened.
|
|
104
|
+
ImportError
|
|
105
|
+
Raised if the task class cannot be imported.
|
|
106
|
+
"""
|
|
107
|
+
from .pipeline_graph import PipelineGraph, visualization
|
|
108
|
+
|
|
109
|
+
# open a file if needed
|
|
110
|
+
close = False
|
|
111
|
+
if not hasattr(file, "write"):
|
|
112
|
+
file = open(file, "w")
|
|
113
|
+
close = True
|
|
114
|
+
|
|
115
|
+
if isinstance(pipeline, Pipeline):
|
|
116
|
+
pg = pipeline.to_graph(visualization_only=True)
|
|
117
|
+
else:
|
|
118
|
+
pg = PipelineGraph()
|
|
119
|
+
for task_def in pipeline:
|
|
120
|
+
pg.add_task(
|
|
121
|
+
task_def.label,
|
|
122
|
+
task_class=task_def.taskClass,
|
|
123
|
+
config=task_def.config,
|
|
124
|
+
connections=task_def.connections,
|
|
125
|
+
)
|
|
126
|
+
pg.resolve(visualization_only=True)
|
|
127
|
+
visualization.show_dot(pg, stream=file, dataset_types=True)
|
|
128
|
+
|
|
129
|
+
if close:
|
|
130
|
+
file.close()
|
|
@@ -27,16 +27,17 @@
|
|
|
27
27
|
|
|
28
28
|
__all__ = ["ExecutionGraphFixup"]
|
|
29
29
|
|
|
30
|
-
import contextlib
|
|
31
30
|
import itertools
|
|
31
|
+
import uuid
|
|
32
32
|
from collections import defaultdict
|
|
33
|
-
from collections.abc import Sequence
|
|
34
|
-
from typing import Any
|
|
33
|
+
from collections.abc import Mapping, Sequence
|
|
35
34
|
|
|
36
35
|
import networkx as nx
|
|
37
36
|
|
|
37
|
+
from lsst.daf.butler import DataCoordinate, DataIdValue
|
|
38
|
+
|
|
38
39
|
from .execution_graph_fixup import ExecutionGraphFixup
|
|
39
|
-
from .graph import QuantumGraph
|
|
40
|
+
from .graph import QuantumGraph
|
|
40
41
|
|
|
41
42
|
|
|
42
43
|
class ExecFixupDataId(ExecutionGraphFixup):
|
|
@@ -88,44 +89,16 @@ class ExecFixupDataId(ExecutionGraphFixup):
|
|
|
88
89
|
else:
|
|
89
90
|
self.dimensions = tuple(self.dimensions)
|
|
90
91
|
|
|
91
|
-
def _key(self, qnode: QuantumNode) -> tuple[Any, ...]:
|
|
92
|
-
"""Produce comparison key for quantum data.
|
|
93
|
-
|
|
94
|
-
Parameters
|
|
95
|
-
----------
|
|
96
|
-
qnode : `QuantumNode`
|
|
97
|
-
An individual node in a `~lsst.pipe.base.QuantumGraph`
|
|
98
|
-
|
|
99
|
-
Returns
|
|
100
|
-
-------
|
|
101
|
-
key : `tuple`
|
|
102
|
-
"""
|
|
103
|
-
dataId = qnode.quantum.dataId
|
|
104
|
-
assert dataId is not None, "Quantum DataId cannot be None"
|
|
105
|
-
key = tuple(dataId[dim] for dim in self.dimensions)
|
|
106
|
-
return key
|
|
107
|
-
|
|
108
92
|
def fixupQuanta(self, graph: QuantumGraph) -> QuantumGraph:
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
for prev_node in keyQuanta[prev_key]:
|
|
122
|
-
for node in keyQuanta[key]:
|
|
123
|
-
# remove any existing edges between the two nodes, but
|
|
124
|
-
# don't fail if there are not any. Both directions need
|
|
125
|
-
# tried because in a directed graph, order maters
|
|
126
|
-
for edge in ((node, prev_node), (prev_node, node)):
|
|
127
|
-
with contextlib.suppress(nx.NetworkXException):
|
|
128
|
-
networkGraph.remove_edge(*edge)
|
|
129
|
-
|
|
130
|
-
networkGraph.add_edge(prev_node, node)
|
|
131
|
-
return graph
|
|
93
|
+
raise NotImplementedError()
|
|
94
|
+
|
|
95
|
+
def fixup_graph(
|
|
96
|
+
self, xgraph: nx.DiGraph, quanta_by_task: Mapping[str, Mapping[DataCoordinate, uuid.UUID]]
|
|
97
|
+
) -> None:
|
|
98
|
+
quanta_by_sort_key: defaultdict[tuple[DataIdValue, ...], list[uuid.UUID]] = defaultdict(list)
|
|
99
|
+
for data_id, quantum_id in quanta_by_task[self.taskLabel].items():
|
|
100
|
+
key = tuple(data_id[dim] for dim in self.dimensions)
|
|
101
|
+
quanta_by_sort_key[key].append(quantum_id)
|
|
102
|
+
sorted_keys = sorted(quanta_by_sort_key.keys(), reverse=self.reverse)
|
|
103
|
+
for prev_key, key in itertools.pairwise(sorted_keys):
|
|
104
|
+
xgraph.add_edges_from(itertools.product(quanta_by_sort_key[prev_key], quanta_by_sort_key[key]))
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# This file is part of pipe_base.
|
|
2
|
+
#
|
|
3
|
+
# Developed for the LSST Data Management System.
|
|
4
|
+
# This product includes software developed by the LSST Project
|
|
5
|
+
# (http://www.lsst.org).
|
|
6
|
+
# See the COPYRIGHT file at the top-level directory of this distribution
|
|
7
|
+
# for details of code ownership.
|
|
8
|
+
#
|
|
9
|
+
# This software is dual licensed under the GNU General Public License and also
|
|
10
|
+
# under a 3-clause BSD license. Recipients may choose which of these licenses
|
|
11
|
+
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
|
|
12
|
+
# respectively. If you choose the GPL option then the following text applies
|
|
13
|
+
# (but note that there is still no warranty even if you opt for BSD instead):
|
|
14
|
+
#
|
|
15
|
+
# This program is free software: you can redistribute it and/or modify
|
|
16
|
+
# it under the terms of the GNU General Public License as published by
|
|
17
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
18
|
+
# (at your option) any later version.
|
|
19
|
+
#
|
|
20
|
+
# This program is distributed in the hope that it will be useful,
|
|
21
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
22
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
23
|
+
# GNU General Public License for more details.
|
|
24
|
+
#
|
|
25
|
+
# You should have received a copy of the GNU General Public License
|
|
26
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
27
|
+
|
|
28
|
+
__all__ = ["ExecutionGraphFixup"]
|
|
29
|
+
|
|
30
|
+
import uuid
|
|
31
|
+
from abc import ABC
|
|
32
|
+
from collections.abc import Mapping
|
|
33
|
+
|
|
34
|
+
import networkx
|
|
35
|
+
|
|
36
|
+
from lsst.daf.butler import DataCoordinate
|
|
37
|
+
|
|
38
|
+
from .graph import QuantumGraph
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ExecutionGraphFixup(ABC):
|
|
42
|
+
"""Interface for classes which update quantum graphs before execution.
|
|
43
|
+
|
|
44
|
+
Notes
|
|
45
|
+
-----
|
|
46
|
+
The primary goal of this class is to modify quanta dependencies which may
|
|
47
|
+
not be possible to reflect in a quantum graph using standard tools. One
|
|
48
|
+
known use case for that is to guarantee particular execution order of
|
|
49
|
+
visits in CI jobs for cases when outcome depends on the processing order of
|
|
50
|
+
visits (e.g. AP association pipeline).
|
|
51
|
+
|
|
52
|
+
Instances of this class receive a preliminary graph and are allowed to
|
|
53
|
+
add edges, as long as those edges do not result in a cycle. Edges and
|
|
54
|
+
nodes may not be removed.
|
|
55
|
+
|
|
56
|
+
New subclasses should implement only `fixup_graph`, which will always be
|
|
57
|
+
called first. `fixupQuanta` is only called if `fixup_graph` raises
|
|
58
|
+
`NotImplementedError`.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
def fixupQuanta(self, graph: QuantumGraph) -> QuantumGraph:
|
|
62
|
+
"""Update quanta in a graph.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
graph : `.QuantumGraph`
|
|
67
|
+
Quantum Graph that will be executed by the executor.
|
|
68
|
+
|
|
69
|
+
Returns
|
|
70
|
+
-------
|
|
71
|
+
graph : `.QuantumGraph`
|
|
72
|
+
Modified graph.
|
|
73
|
+
|
|
74
|
+
Notes
|
|
75
|
+
-----
|
|
76
|
+
This hook is provided for backwards compatibility only.
|
|
77
|
+
"""
|
|
78
|
+
raise NotImplementedError()
|
|
79
|
+
|
|
80
|
+
def fixup_graph(
|
|
81
|
+
self, xgraph: networkx.DiGraph, quanta_by_task: Mapping[str, Mapping[DataCoordinate, uuid.UUID]]
|
|
82
|
+
) -> None:
|
|
83
|
+
"""Update a networkx graph of quanta in place by adding edges to
|
|
84
|
+
further constrain the ordering.
|
|
85
|
+
|
|
86
|
+
Parameters
|
|
87
|
+
----------
|
|
88
|
+
xgraph : `networkx.DiGraph`
|
|
89
|
+
A directed acyclic graph of quanta to modify in place. Node keys
|
|
90
|
+
are quantum UUIDs, and attributes include ``task_label`` (`str`)
|
|
91
|
+
and ``data_id`` (a full `lsst.daf.butler.DataCoordinate`, without
|
|
92
|
+
dimension records attached). Edges may be added, but not removed.
|
|
93
|
+
Nodes may not be modified.
|
|
94
|
+
quanta_by_task : `~collections.abc.Mapping` [ `str`,\
|
|
95
|
+
`~collections.abc.Mapping` [ `lsst.daf.butler.DataCoordinate`,\
|
|
96
|
+
`uuid.UUID` ] ]
|
|
97
|
+
All quanta in the graph, grouped first by task label and then by
|
|
98
|
+
data ID.
|
|
99
|
+
"""
|
|
100
|
+
raise NotImplementedError()
|
|
@@ -38,7 +38,7 @@ from collections import defaultdict
|
|
|
38
38
|
from collections.abc import Callable
|
|
39
39
|
from dataclasses import dataclass
|
|
40
40
|
from types import SimpleNamespace
|
|
41
|
-
from typing import TYPE_CHECKING, ClassVar
|
|
41
|
+
from typing import TYPE_CHECKING, ClassVar
|
|
42
42
|
|
|
43
43
|
import networkx as nx
|
|
44
44
|
|
|
@@ -50,6 +50,7 @@ from lsst.daf.butler import (
|
|
|
50
50
|
Quantum,
|
|
51
51
|
SerializedDimensionRecord,
|
|
52
52
|
)
|
|
53
|
+
from lsst.daf.butler._rubin import generate_uuidv7
|
|
53
54
|
from lsst.utils import doImportType
|
|
54
55
|
|
|
55
56
|
from ..config import PipelineTaskConfig
|
|
@@ -242,7 +243,7 @@ class DeserializerV1(DeserializerBase):
|
|
|
242
243
|
|
|
243
244
|
# reconstruct node
|
|
244
245
|
qNode = pickle.loads(dump)
|
|
245
|
-
object.__setattr__(qNode, "nodeId",
|
|
246
|
+
object.__setattr__(qNode, "nodeId", generate_uuidv7())
|
|
246
247
|
|
|
247
248
|
# read the saved node, name. If it has been loaded, attach it, if
|
|
248
249
|
# not read in the taskDef first, and then load it
|
|
@@ -376,7 +377,7 @@ class DeserializerV2(DeserializerBase):
|
|
|
376
377
|
|
|
377
378
|
# reconstruct node
|
|
378
379
|
qNode = pickle.loads(dump)
|
|
379
|
-
object.__setattr__(qNode, "nodeId",
|
|
380
|
+
object.__setattr__(qNode, "nodeId", generate_uuidv7())
|
|
380
381
|
|
|
381
382
|
# read the saved node, name. If it has been loaded, attach it, if
|
|
382
383
|
# not read in the taskDef first, and then load it
|
|
@@ -599,11 +600,11 @@ class DeserializerV3(DeserializerBase):
|
|
|
599
600
|
# initInputRefs and initOutputRefs are optional
|
|
600
601
|
if (refs := taskDefDump.get("initInputRefs")) is not None:
|
|
601
602
|
initInputRefs[recreatedTaskDef.label] = [
|
|
602
|
-
|
|
603
|
+
DatasetRef.from_json(ref, universe=universe) for ref in refs
|
|
603
604
|
]
|
|
604
605
|
if (refs := taskDefDump.get("initOutputRefs")) is not None:
|
|
605
606
|
initOutputRefs[recreatedTaskDef.label] = [
|
|
606
|
-
|
|
607
|
+
DatasetRef.from_json(ref, universe=universe) for ref in refs
|
|
607
608
|
]
|
|
608
609
|
|
|
609
610
|
# rebuild the mappings that associate dataset type names with
|
{lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/graph.py
RENAMED
|
@@ -59,6 +59,7 @@ from lsst.daf.butler import (
|
|
|
59
59
|
Quantum,
|
|
60
60
|
QuantumBackedButler,
|
|
61
61
|
)
|
|
62
|
+
from lsst.daf.butler._rubin import generate_uuidv7
|
|
62
63
|
from lsst.daf.butler.datastore.record_data import DatastoreRecordData
|
|
63
64
|
from lsst.daf.butler.persistence_context import PersistenceContextVars
|
|
64
65
|
from lsst.daf.butler.registry import ConflictingDefinitionError
|
|
@@ -246,7 +247,7 @@ class QuantumGraph:
|
|
|
246
247
|
"associated value in the mapping"
|
|
247
248
|
)
|
|
248
249
|
else:
|
|
249
|
-
nodeId =
|
|
250
|
+
nodeId = generate_uuidv7()
|
|
250
251
|
|
|
251
252
|
inits = quantum.initInputs.values()
|
|
252
253
|
inputs = quantum.inputs.values()
|
|
@@ -806,11 +807,18 @@ class QuantumGraph:
|
|
|
806
807
|
uri : convertible to `~lsst.resources.ResourcePath`
|
|
807
808
|
URI to where the graph should be saved.
|
|
808
809
|
"""
|
|
809
|
-
buffer = self._buildSaveObject()
|
|
810
810
|
path = ResourcePath(uri)
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
811
|
+
match path.getExtension():
|
|
812
|
+
case ".qgraph":
|
|
813
|
+
buffer = self._buildSaveObject()
|
|
814
|
+
path.write(buffer) # type: ignore # Ignore because bytearray is safe to use in place of bytes
|
|
815
|
+
case ".qg":
|
|
816
|
+
from ..quantum_graph import PredictedQuantumGraphComponents
|
|
817
|
+
|
|
818
|
+
pqg = PredictedQuantumGraphComponents.from_old_quantum_graph(self)
|
|
819
|
+
pqg.write(path)
|
|
820
|
+
case ext:
|
|
821
|
+
raise TypeError(f"Can currently only save a graph in .qgraph or .qg format, not {ext!r}.")
|
|
814
822
|
|
|
815
823
|
@property
|
|
816
824
|
def metadata(self) -> MappingProxyType[str, Any]:
|
|
@@ -962,11 +970,23 @@ class QuantumGraph:
|
|
|
962
970
|
the graph.
|
|
963
971
|
"""
|
|
964
972
|
uri = ResourcePath(uri)
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
973
|
+
match uri.getExtension():
|
|
974
|
+
case ".qgraph":
|
|
975
|
+
with LoadHelper(uri, minimumVersion, fullRead=(nodes is None)) as loader:
|
|
976
|
+
qgraph = loader.load(universe, nodes, graphID)
|
|
977
|
+
case ".qg":
|
|
978
|
+
from ..quantum_graph import PredictedQuantumGraphReader
|
|
979
|
+
|
|
980
|
+
with PredictedQuantumGraphReader.open(uri, page_size=100000) as qgr:
|
|
981
|
+
quantum_ids = (
|
|
982
|
+
[uuid.UUID(q) if not isinstance(q, uuid.UUID) else q for q in nodes]
|
|
983
|
+
if nodes is not None
|
|
984
|
+
else None
|
|
985
|
+
)
|
|
986
|
+
qgr.read_execution_quanta(quantum_ids)
|
|
987
|
+
qgraph = qgr.finish().to_old_quantum_graph()
|
|
988
|
+
case _:
|
|
989
|
+
raise ValueError(f"Only know how to handle files saved as `.qgraph`, not {uri}")
|
|
970
990
|
if not isinstance(qgraph, QuantumGraph):
|
|
971
991
|
raise TypeError(f"QuantumGraph file {uri} contains unexpected object type: {type(qgraph)}")
|
|
972
992
|
return qgraph
|
|
@@ -75,6 +75,21 @@ class QgraphTaskSummary(pydantic.BaseModel):
|
|
|
75
75
|
"""See `pydantic.BaseModel.model_json_schema`."""
|
|
76
76
|
return super().model_json_schema(*args, **kwargs)
|
|
77
77
|
|
|
78
|
+
@classmethod
|
|
79
|
+
def model_validate(cls, *args: Any, **kwargs: Any) -> Any:
|
|
80
|
+
"""See `pydantic.BaseModel.model_validate`."""
|
|
81
|
+
return super().model_validate(*args, **kwargs)
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def model_validate_json(cls, *args: Any, **kwargs: Any) -> Any:
|
|
85
|
+
"""See `pydantic.BaseModel.model_validate_json`."""
|
|
86
|
+
return super().model_validate_json(*args, **kwargs)
|
|
87
|
+
|
|
88
|
+
@classmethod
|
|
89
|
+
def model_validate_strings(cls, *args: Any, **kwargs: Any) -> Any:
|
|
90
|
+
"""See `pydantic.BaseModel.model_validate_strings`."""
|
|
91
|
+
return super().model_validate_strings(*args, **kwargs)
|
|
92
|
+
|
|
78
93
|
|
|
79
94
|
class QgraphSummary(pydantic.BaseModel):
|
|
80
95
|
"""Report for the QuantumGraph creation or reading."""
|
|
@@ -129,3 +144,18 @@ class QgraphSummary(pydantic.BaseModel):
|
|
|
129
144
|
def model_json_schema(cls, *args: Any, **kwargs: Any) -> Any:
|
|
130
145
|
"""See `pydantic.BaseModel.model_json_schema`."""
|
|
131
146
|
return super().model_json_schema(*args, **kwargs)
|
|
147
|
+
|
|
148
|
+
@classmethod
|
|
149
|
+
def model_validate(cls, *args: Any, **kwargs: Any) -> Any:
|
|
150
|
+
"""See `pydantic.BaseModel.model_validate`."""
|
|
151
|
+
return super().model_validate(*args, **kwargs)
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def model_validate_json(cls, *args: Any, **kwargs: Any) -> Any:
|
|
155
|
+
"""See `pydantic.BaseModel.model_validate_json`."""
|
|
156
|
+
return super().model_validate_json(*args, **kwargs)
|
|
157
|
+
|
|
158
|
+
@classmethod
|
|
159
|
+
def model_validate_strings(cls, *args: Any, **kwargs: Any) -> Any:
|
|
160
|
+
"""See `pydantic.BaseModel.model_validate_strings`."""
|
|
161
|
+
return super().model_validate_strings(*args, **kwargs)
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# This file is part of pipe_base.
|
|
2
|
+
#
|
|
3
|
+
# Developed for the LSST Data Management System.
|
|
4
|
+
# This product includes software developed by the LSST Project
|
|
5
|
+
# (http://www.lsst.org).
|
|
6
|
+
# See the COPYRIGHT file at the top-level directory of this distribution
|
|
7
|
+
# for details of code ownership.
|
|
8
|
+
#
|
|
9
|
+
# This software is dual licensed under the GNU General Public License and also
|
|
10
|
+
# under a 3-clause BSD license. Recipients may choose which of these licenses
|
|
11
|
+
# to use; please see the files gpl-3.0.txt and/or bsd_license.txt,
|
|
12
|
+
# respectively. If you choose the GPL option then the following text applies
|
|
13
|
+
# (but note that there is still no warranty even if you opt for BSD instead):
|
|
14
|
+
#
|
|
15
|
+
# This program is free software: you can redistribute it and/or modify
|
|
16
|
+
# it under the terms of the GNU General Public License as published by
|
|
17
|
+
# the Free Software Foundation, either version 3 of the License, or
|
|
18
|
+
# (at your option) any later version.
|
|
19
|
+
#
|
|
20
|
+
# This program is distributed in the hope that it will be useful,
|
|
21
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
22
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
23
|
+
# GNU General Public License for more details.
|
|
24
|
+
#
|
|
25
|
+
# You should have received a copy of the GNU General Public License
|
|
26
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
__all__ = ("GraphWalker",)
|
|
31
|
+
|
|
32
|
+
from typing import Generic, Self, TypeVar
|
|
33
|
+
|
|
34
|
+
import networkx
|
|
35
|
+
|
|
36
|
+
_T = TypeVar("_T")
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class GraphWalker(Generic[_T]):
|
|
40
|
+
"""A helper for traversing directed acyclic graphs.
|
|
41
|
+
|
|
42
|
+
Parameters
|
|
43
|
+
----------
|
|
44
|
+
xgraph : `networkx.DiGraph` or `networkx.MultiDiGraph`
|
|
45
|
+
Networkx graph to process. Will be consumed during iteration, so this
|
|
46
|
+
should often be a copy.
|
|
47
|
+
|
|
48
|
+
Notes
|
|
49
|
+
-----
|
|
50
|
+
Each iteration yields a `frozenset` of nodes, which may be empty if there
|
|
51
|
+
are no nodes ready for processing. A node is only considered ready if all
|
|
52
|
+
of its predecessor nodes have been marked as complete with `finish`.
|
|
53
|
+
Iteration only completes when all nodes have been finished or failed.
|
|
54
|
+
|
|
55
|
+
`GraphWalker` is not thread-safe; calling one `GraphWalker` method while
|
|
56
|
+
another is in progress is undefined behavior. It is designed to be used
|
|
57
|
+
in the management thread or process in a parallel traversal.
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def __init__(self, xgraph: networkx.DiGraph | networkx.MultiDiGraph):
|
|
61
|
+
self._xgraph = xgraph
|
|
62
|
+
self._ready: set[_T] = set(next(iter(networkx.dag.topological_generations(self._xgraph)), []))
|
|
63
|
+
self._active: set[_T] = set()
|
|
64
|
+
self._incomplete: set[_T] = set(self._xgraph)
|
|
65
|
+
|
|
66
|
+
def __iter__(self) -> Self:
|
|
67
|
+
return self
|
|
68
|
+
|
|
69
|
+
def __next__(self) -> frozenset[_T]:
|
|
70
|
+
if not self._incomplete:
|
|
71
|
+
raise StopIteration()
|
|
72
|
+
new_active = frozenset(self._ready)
|
|
73
|
+
self._active.update(new_active)
|
|
74
|
+
self._ready.clear()
|
|
75
|
+
return new_active
|
|
76
|
+
|
|
77
|
+
def finish(self, key: _T) -> None:
|
|
78
|
+
"""Mark a node as successfully processed, unblocking (at least in part)
|
|
79
|
+
iteration over successor nodes.
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
key : unspecified
|
|
84
|
+
NetworkX key of the node to mark finished.
|
|
85
|
+
"""
|
|
86
|
+
self._active.remove(key)
|
|
87
|
+
self._incomplete.remove(key)
|
|
88
|
+
successors = list(self._xgraph.successors(key))
|
|
89
|
+
for successor in successors:
|
|
90
|
+
assert successor not in self._active, (
|
|
91
|
+
"A node downstream of an active one should not have been yielded yet."
|
|
92
|
+
)
|
|
93
|
+
if all(
|
|
94
|
+
predecessor not in self._incomplete for predecessor in self._xgraph.predecessors(successor)
|
|
95
|
+
):
|
|
96
|
+
self._ready.add(successor)
|
|
97
|
+
|
|
98
|
+
def fail(self, key: _T) -> list[_T]:
|
|
99
|
+
"""Mark a node as unsuccessfully processed, permanently blocking all
|
|
100
|
+
recursive descendants.
|
|
101
|
+
|
|
102
|
+
Parameters
|
|
103
|
+
----------
|
|
104
|
+
key : unspecified
|
|
105
|
+
NetworkX key of the node to mark as a failure.
|
|
106
|
+
|
|
107
|
+
Returns
|
|
108
|
+
-------
|
|
109
|
+
blocked : `list`
|
|
110
|
+
NetworkX keys of nodes that were recursive descendants of the
|
|
111
|
+
failed node, and will hence never be yielded by the iterator.
|
|
112
|
+
"""
|
|
113
|
+
self._active.remove(key)
|
|
114
|
+
self._incomplete.remove(key)
|
|
115
|
+
descendants = list(networkx.dag.descendants(self._xgraph, key))
|
|
116
|
+
self._xgraph.remove_node(key)
|
|
117
|
+
self._xgraph.remove_nodes_from(descendants)
|
|
118
|
+
self._incomplete.difference_update(descendants)
|
|
119
|
+
return descendants
|
{lsst_pipe_base-29.2025.3900 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/log_capture.py
RENAMED
|
@@ -41,6 +41,7 @@ from lsst.daf.butler import Butler, FileDataset, LimitedButler, Quantum
|
|
|
41
41
|
from lsst.daf.butler.logging import ButlerLogRecordHandler, ButlerLogRecords, ButlerMDC, JsonLogFormatter
|
|
42
42
|
|
|
43
43
|
from ._status import InvalidQuantumError
|
|
44
|
+
from .automatic_connection_constants import METADATA_OUTPUT_TEMPLATE
|
|
44
45
|
from .pipeline_graph import TaskNode
|
|
45
46
|
|
|
46
47
|
_LOG = logging.getLogger(__name__)
|
|
@@ -116,8 +117,10 @@ class LogCapture:
|
|
|
116
117
|
mdc = {"LABEL": task_node.label, "RUN": ""}
|
|
117
118
|
if quantum.dataId:
|
|
118
119
|
mdc["LABEL"] += f":{quantum.dataId}"
|
|
119
|
-
|
|
120
|
-
|
|
120
|
+
|
|
121
|
+
metadata_ref = quantum.outputs[METADATA_OUTPUT_TEMPLATE.format(label=task_node.label)][0]
|
|
122
|
+
mdc["RUN"] = metadata_ref.run
|
|
123
|
+
|
|
121
124
|
ctx = _LogCaptureFlag()
|
|
122
125
|
log_dataset_name = (
|
|
123
126
|
task_node.log_output.dataset_type_name if task_node.log_output is not None else None
|