lsst-pipe-base 29.2025.1400__tar.gz → 29.2025.1500__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.1400/python/lsst_pipe_base.egg-info → lsst_pipe_base-29.2025.1500}/PKG-INFO +2 -1
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/working-with-pipeline-graphs.rst +99 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/pyproject.toml +3 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/__main__.py +1 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/_exceptions.py +7 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/_pipeline_graph.py +360 -11
- lsst_pipe_base-29.2025.1500/python/lsst/pipe/base/pipeline_graph/expressions.py +271 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/visualization/__init__.py +1 -0
- lsst_pipe_base-29.2025.1500/python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py +529 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/visualization/_options.py +11 -3
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/visualization/_show.py +23 -3
- lsst_pipe_base-29.2025.1500/python/lsst/pipe/base/pipeline_graph/visualization/_status_annotator.py +250 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/quantum_provenance_graph.py +28 -0
- lsst_pipe_base-29.2025.1500/python/lsst/pipe/base/version.py +2 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500/python/lsst_pipe_base.egg-info}/PKG-INFO +2 -1
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst_pipe_base.egg-info/SOURCES.txt +3 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst_pipe_base.egg-info/requires.txt +1 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_pipeline_graph.py +119 -0
- lsst_pipe_base-29.2025.1500/tests/test_pipeline_graph_expressions.py +127 -0
- lsst_pipe_base-29.2025.1400/python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py +0 -234
- lsst_pipe_base-29.2025.1400/python/lsst/pipe/base/version.py +0 -2
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/COPYRIGHT +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/LICENSE +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/MANIFEST.in +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/README.md +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/bsd_license.txt +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/CHANGES.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/creating-a-pipeline.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/creating-a-pipelinetask.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/creating-a-task.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/index.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/task-framework-overview.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/task-retargeting-howto.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/testing-a-pipeline-task.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/doc/lsst.pipe.base/testing-pipelines-with-mocks.rst +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/gpl-v3.0.txt +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/_datasetQueryConstraints.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/_dataset_handle.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/_instrument.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/_observation_dimension_packer.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/_quantumContext.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/_status.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/_task_metadata.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/all_dimensions_quantum_graph_builder.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/automatic_connection_constants.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/caching_limited_butler.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/cli/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/cli/_get_cli_subcommands.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/cli/cmd/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/cli/cmd/commands.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/cli/opt/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/cli/opt/arguments.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/cli/opt/options.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/config.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/configOverrides.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/connectionTypes.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/connections.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/dot_tools.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/executionButlerBuilder.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/execution_reports.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/formatters/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/formatters/pexConfig.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/graph/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/graph/_implDetails.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/graph/_loadHelpers.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/graph/_versionDeserializers.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/graph/graph.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/graph/graphSummary.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/graph/quantumNode.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/mermaid_tools.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipelineIR.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipelineTask.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/_dataset_types.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/_edges.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/_mapping_views.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/_nodes.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/_task_subsets.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/_tasks.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/io.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/visualization/_dot.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/visualization/_layout.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/visualization/_merge.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/visualization/_mermaid.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/pipeline_graph/visualization/_printer.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/prerequisite_helpers.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/py.typed +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/quantum_graph_builder.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/quantum_graph_skeleton.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/script/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/script/register_instrument.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/script/retrieve_artifacts_for_quanta.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/script/transfer_from_graph.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/script/zip_from_graph.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/struct.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/task.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/taskFactory.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/testUtils.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/mocks/__init__.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/mocks/_data_id_match.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/mocks/_pipeline_task.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/mocks/_storage_class.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/no_dimensions.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/pipelineStepTester.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/simpleQGraph.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/tests/util.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst/pipe/base/utils.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst_pipe_base.egg-info/dependency_links.txt +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst_pipe_base.egg-info/entry_points.txt +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst_pipe_base.egg-info/top_level.txt +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/python/lsst_pipe_base.egg-info/zip-safe +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/setup.cfg +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_adjust_all_quanta.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_caching_limited_butler.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_cliCmdRegisterInstrument.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_configOverrides.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_config_formatter.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_connections.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_dataid_match.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_dataset_handle.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_dot_tools.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_dynamic_connections.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_executionButler.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_execution_reports.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_graphBuilder.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_init_output_run.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_instrument.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_mermaid.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_pipeline.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_pipelineIR.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_pipelineLoadSubset.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_pipelineTask.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_quantumGraph.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_quantum_provenance_graph.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_quantum_success_caveats.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_struct.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_task.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_taskmetadata.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_testUtils.py +0 -0
- {lsst_pipe_base-29.2025.1400 → lsst_pipe_base-29.2025.1500}/tests/test_utils.py +0 -0
{lsst_pipe_base-29.2025.1400/python/lsst_pipe_base.egg-info → lsst_pipe_base-29.2025.1500}/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.1500
|
|
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
|
|
@@ -27,6 +27,7 @@ Requires-Dist: lsst-pex-config
|
|
|
27
27
|
Requires-Dist: astropy
|
|
28
28
|
Requires-Dist: pydantic<3.0,>=2
|
|
29
29
|
Requires-Dist: networkx
|
|
30
|
+
Requires-Dist: wcwidth
|
|
30
31
|
Requires-Dist: pyyaml>=5.1
|
|
31
32
|
Requires-Dist: numpy>=1.17
|
|
32
33
|
Requires-Dist: frozendict
|
|
@@ -86,3 +86,102 @@ The export methods include:
|
|
|
86
86
|
This is a `networkx.DiGraph`, because all dataset types that connect a pair of tasks are rolled into one edge, and edges have no state.
|
|
87
87
|
- `~PipelineGraph.make_dataset_type_graph` exports just dataset type nodes; it is one "bipartite projection" of the full graph.
|
|
88
88
|
This is a `networkx.DiGraph`, because all tasks that connect a pair of dataset types are rolled into one edge, and edges have no state.
|
|
89
|
+
|
|
90
|
+
.. _pipeline-graph-subset-expressions:
|
|
91
|
+
|
|
92
|
+
Pipeline graph subset expressions
|
|
93
|
+
---------------------------------
|
|
94
|
+
|
|
95
|
+
The `PipelineGraph.select` and `PipelineGraph.select_tasks` methods utilize a boolean expression language to select a subset of the tasks in a `PipelineGraph`.
|
|
96
|
+
The language uses familiar set operators for union (``|``), intersection (``&``), and set-inversion (``~``), with the operands any of the following:
|
|
97
|
+
|
|
98
|
+
- a task label
|
|
99
|
+
- a task subset label
|
|
100
|
+
- a dataset type name (resolves to the label of the producing task, or an empty set for overall inputs; may not be an init-output)
|
|
101
|
+
- an ancestor or descendant search, starting from a task label or dataset type name (see below)
|
|
102
|
+
- a nested expression.
|
|
103
|
+
|
|
104
|
+
Parentheses may be used for grouping.
|
|
105
|
+
|
|
106
|
+
Task labels, task subset labels, and dataset type names all appear as regular unquoted strings.
|
|
107
|
+
In cases where dataset type name is the same as a task or task subset label, a prefix can be added to disambiguate: ``T:`` for task, ``D:`` for dataset type, and ``S:`` for task subset.
|
|
108
|
+
|
|
109
|
+
An ancestor or descendant search uses ``<``, ``<=``, ``>``, and ``>=`` as *unary* operators, with the operands being task labels or dataset type names (which may be qualified with ``T:`` or ``D:``, respectively, as described above).
|
|
110
|
+
For tasks these searches are straightforward:
|
|
111
|
+
|
|
112
|
+
- ``<`` and ``<=`` select all tasks whose outputs are consumed by the operand task, recursively, with the operand task itself included only for ``<=``.
|
|
113
|
+
|
|
114
|
+
- ``>`` and ``>=`` select all tasks that consume the outputs of the operand task, rescursively, with the operand task itself included only for ``>=``.
|
|
115
|
+
|
|
116
|
+
Because the expressions are logically set operations on tasks, ancestor and descendant searches on dataset types work differently and are not quite symmetric:
|
|
117
|
+
|
|
118
|
+
- ``<`` and ``<=`` act like an ancestor search on the task that produces the operand dataset type.
|
|
119
|
+
For overall inputs they yield empty sets.
|
|
120
|
+
Init-outputs are not permitted.
|
|
121
|
+
|
|
122
|
+
- ``>`` and ``>=`` act like a union of descendant searches on all tasks that consume the operand dataset type.
|
|
123
|
+
This includes tasks that consume the operand dataset type as an init-input (this is the only context in which init-output dataset types can appear in expressions).
|
|
124
|
+
For ``>=`` only, the task that produces the operand dataset type is also included, but in this case it is an error for the operand to be an init-output.
|
|
125
|
+
|
|
126
|
+
Note that these ancestor and descendant searches are not the only useful way to define the subset of a pipeline that is "before" or "after" a task; the ancestors ``<a`` of a task ``a`` are those that *must* be run before ``a``, while the inverse of the descendants ``~>=a`` are the tasks that *can* be run before ``a``.
|
|
127
|
+
Similarly, the descendants ``>a`` of ``a`` are the tasks that can only be run after ``a``, while the inverse of the ancestors ``~<=a`` are all tasks that can be run after ``a``.
|
|
128
|
+
|
|
129
|
+
Examples
|
|
130
|
+
^^^^^^^^
|
|
131
|
+
|
|
132
|
+
All tasks in subset ``s`` except task ``b``:
|
|
133
|
+
|
|
134
|
+
.. code-block:: text
|
|
135
|
+
|
|
136
|
+
s & ~b
|
|
137
|
+
|
|
138
|
+
All tasks in either subset ``r`` or subset ``s`` that would need to be re-run to pick up a change in the behavior of task ``a``:
|
|
139
|
+
|
|
140
|
+
.. code-block:: text
|
|
141
|
+
|
|
142
|
+
(r | s) & >=a
|
|
143
|
+
|
|
144
|
+
All tasks in subset ``s`` that need to be run to accept failures in task ``c`` as unrecoverable, after a previous run left some quanta of those tasks blocked:
|
|
145
|
+
|
|
146
|
+
.. code-block:: text
|
|
147
|
+
|
|
148
|
+
s & >a
|
|
149
|
+
|
|
150
|
+
All tasks needed to produce dataset type ``d`` or dataset type ``e``:
|
|
151
|
+
|
|
152
|
+
.. code-block:: text
|
|
153
|
+
|
|
154
|
+
<d | <e
|
|
155
|
+
|
|
156
|
+
All tasks except task ``a`` that can be run without producing dataset type ``f``:
|
|
157
|
+
|
|
158
|
+
.. code-block:: text
|
|
159
|
+
|
|
160
|
+
~a & ~>=f
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
Formal grammar
|
|
164
|
+
^^^^^^^^^^^^^^
|
|
165
|
+
|
|
166
|
+
.. code-block:: bnf
|
|
167
|
+
|
|
168
|
+
<expression> ::= ~ <expression>
|
|
169
|
+
| <expression> | <expression>
|
|
170
|
+
| <expression> & <expression>
|
|
171
|
+
| (<expression>)
|
|
172
|
+
| S:<subset-label>
|
|
173
|
+
| <subset-label>
|
|
174
|
+
| < <node>
|
|
175
|
+
| <= <node>
|
|
176
|
+
| > <node>
|
|
177
|
+
| <= <node>
|
|
178
|
+
| <node>
|
|
179
|
+
|
|
180
|
+
<node> ::= T:<task-label>
|
|
181
|
+
| <task-label>
|
|
182
|
+
| D:<dataset-type-name>
|
|
183
|
+
| <dataset-type-name>
|
|
184
|
+
|
|
185
|
+
Whitespace is ignored, but is not permitted before or after the ``:`` in qualified identifiers.
|
|
186
|
+
|
|
187
|
+
The operator precedence in the absence of parenthesis is ``~``, ``&``, ``|`` ( highest to lowest).
|
|
@@ -30,6 +30,7 @@ dependencies = [
|
|
|
30
30
|
"astropy",
|
|
31
31
|
"pydantic >=2,<3.0",
|
|
32
32
|
"networkx",
|
|
33
|
+
"wcwidth",
|
|
33
34
|
"pyyaml >= 5.1",
|
|
34
35
|
"numpy >= 1.17",
|
|
35
36
|
"frozendict",
|
|
@@ -205,4 +206,6 @@ exclude = [
|
|
|
205
206
|
"^test_.*", # Do not test docstrings in test code.
|
|
206
207
|
'^commands\.', # Click docstrings, not numpydoc
|
|
207
208
|
'\._[a-zA-Z_]+$', # Private methods.
|
|
209
|
+
'_ParserLex\.', # Docstrings are not numpydoc
|
|
210
|
+
'_ParserYacc\.', # Docstrings are not numpydoc
|
|
208
211
|
]
|
|
@@ -31,6 +31,7 @@ __all__ = (
|
|
|
31
31
|
"DuplicateOutputError",
|
|
32
32
|
"EdgesChangedError",
|
|
33
33
|
"IncompatibleDatasetTypeError",
|
|
34
|
+
"InvalidExpressionError",
|
|
34
35
|
"InvalidStepsError",
|
|
35
36
|
"PipelineDataCycleError",
|
|
36
37
|
"PipelineGraphError",
|
|
@@ -102,5 +103,11 @@ class PipelineGraphExceptionSafetyError(PipelineGraphError):
|
|
|
102
103
|
"""
|
|
103
104
|
|
|
104
105
|
|
|
106
|
+
class InvalidExpressionError(PipelineGraphError):
|
|
107
|
+
"""Exception raised when a pipeline subset expression could not be parsed
|
|
108
|
+
or applied.
|
|
109
|
+
"""
|
|
110
|
+
|
|
111
|
+
|
|
105
112
|
class InvalidStepsError(PipelineGraphError):
|
|
106
113
|
"""Exception raised when the step definitions are invalid."""
|
|
@@ -55,11 +55,13 @@ from lsst.utils.packages import Packages
|
|
|
55
55
|
|
|
56
56
|
from .._dataset_handle import InMemoryDatasetHandle
|
|
57
57
|
from ..automatic_connection_constants import PACKAGES_INIT_OUTPUT_NAME, PACKAGES_INIT_OUTPUT_STORAGE_CLASS
|
|
58
|
+
from . import expressions
|
|
58
59
|
from ._dataset_types import DatasetTypeNode
|
|
59
60
|
from ._edges import Edge, ReadEdge, WriteEdge
|
|
60
61
|
from ._exceptions import (
|
|
61
62
|
DuplicateOutputError,
|
|
62
63
|
EdgesChangedError,
|
|
64
|
+
InvalidExpressionError,
|
|
63
65
|
InvalidStepsError,
|
|
64
66
|
PipelineDataCycleError,
|
|
65
67
|
PipelineGraphError,
|
|
@@ -1149,16 +1151,7 @@ class PipelineGraph:
|
|
|
1149
1151
|
See `TaskNode` and `TaskInitNode` for the descriptive node and
|
|
1150
1152
|
attributes added.
|
|
1151
1153
|
"""
|
|
1152
|
-
|
|
1153
|
-
task_keys = [
|
|
1154
|
-
key
|
|
1155
|
-
for key, bipartite in bipartite_xgraph.nodes(data="bipartite")
|
|
1156
|
-
if bipartite == NodeType.TASK.bipartite
|
|
1157
|
-
]
|
|
1158
|
-
return self._transform_xgraph_state(
|
|
1159
|
-
networkx.algorithms.bipartite.projected_graph(networkx.DiGraph(bipartite_xgraph), task_keys),
|
|
1160
|
-
skip_edges=True,
|
|
1161
|
-
)
|
|
1154
|
+
return self._transform_xgraph_state(self._make_task_xgraph_internal(init), skip_edges=True)
|
|
1162
1155
|
|
|
1163
1156
|
def make_dataset_type_xgraph(self, init: bool = False) -> networkx.DiGraph:
|
|
1164
1157
|
"""Return a networkx representation of just the dataset types in the
|
|
@@ -1197,6 +1190,62 @@ class PipelineGraph:
|
|
|
1197
1190
|
skip_edges=True,
|
|
1198
1191
|
)
|
|
1199
1192
|
|
|
1193
|
+
###########################################################################
|
|
1194
|
+
#
|
|
1195
|
+
# Expression-based Selection Interface.
|
|
1196
|
+
#
|
|
1197
|
+
###########################################################################
|
|
1198
|
+
|
|
1199
|
+
def select_tasks(self, expression: str) -> set[str]:
|
|
1200
|
+
"""Return the tasks that match an expression.
|
|
1201
|
+
|
|
1202
|
+
Parameters
|
|
1203
|
+
----------
|
|
1204
|
+
expression : `str`
|
|
1205
|
+
String expression to evaluate. See
|
|
1206
|
+
:ref:`pipeline-graph-subset-expressions`.
|
|
1207
|
+
|
|
1208
|
+
Returns
|
|
1209
|
+
-------
|
|
1210
|
+
task_labels : `set` [ `str` ]
|
|
1211
|
+
Set of matching task labels.
|
|
1212
|
+
"""
|
|
1213
|
+
task_xgraph = self._make_task_xgraph_internal(init=False)
|
|
1214
|
+
expr_tree = expressions.parse(expression)
|
|
1215
|
+
matching_task_keys = self._select_expression(expr_tree, task_xgraph)
|
|
1216
|
+
return {key.name for key in matching_task_keys}
|
|
1217
|
+
|
|
1218
|
+
def select(self, expression: str) -> PipelineGraph:
|
|
1219
|
+
"""Return a new pipeline graph with the tasks that match an expression.
|
|
1220
|
+
|
|
1221
|
+
Parameters
|
|
1222
|
+
----------
|
|
1223
|
+
expression : `str`
|
|
1224
|
+
String expression to evaluate. See
|
|
1225
|
+
:ref:`pipeline-graph-subset-expressions`.
|
|
1226
|
+
|
|
1227
|
+
Returns
|
|
1228
|
+
-------
|
|
1229
|
+
new_graph : `PipelineGraph`
|
|
1230
|
+
New pipeline graph with just the matching tasks.
|
|
1231
|
+
|
|
1232
|
+
Notes
|
|
1233
|
+
-----
|
|
1234
|
+
All resolved dataset type nodes will be preserved.
|
|
1235
|
+
|
|
1236
|
+
If `has_been_sorted`, the new graph will be sorted as well.
|
|
1237
|
+
|
|
1238
|
+
Task subsets will not be included in the returned graph.
|
|
1239
|
+
"""
|
|
1240
|
+
selected_tasks = self.select_tasks(expression)
|
|
1241
|
+
new_pipeline_graph = PipelineGraph(universe=self._universe, data_id=self._raw_data_id)
|
|
1242
|
+
new_pipeline_graph.add_task_nodes(
|
|
1243
|
+
[self.tasks[task_label] for task_label in selected_tasks], parent=self
|
|
1244
|
+
)
|
|
1245
|
+
if self.has_been_sorted:
|
|
1246
|
+
new_pipeline_graph.sort()
|
|
1247
|
+
return new_pipeline_graph
|
|
1248
|
+
|
|
1200
1249
|
###########################################################################
|
|
1201
1250
|
#
|
|
1202
1251
|
# Serialization Interface.
|
|
@@ -1575,6 +1624,8 @@ class PipelineGraph:
|
|
|
1575
1624
|
element in the iterable.
|
|
1576
1625
|
|
|
1577
1626
|
If `has_been_sorted`, all subgraphs will be sorted as well.
|
|
1627
|
+
|
|
1628
|
+
Task subsets will not be included in the returned graphs.
|
|
1578
1629
|
"""
|
|
1579
1630
|
# Having an overall input in common isn't enough to make subgraphs
|
|
1580
1631
|
# dependent on each other, so we want to look for connected component
|
|
@@ -1595,7 +1646,7 @@ class PipelineGraph:
|
|
|
1595
1646
|
yield self
|
|
1596
1647
|
return
|
|
1597
1648
|
else:
|
|
1598
|
-
component_subgraph = PipelineGraph(universe=self._universe)
|
|
1649
|
+
component_subgraph = PipelineGraph(universe=self._universe, data_id=self._raw_data_id)
|
|
1599
1650
|
component_subgraph.add_task_nodes(
|
|
1600
1651
|
[self._xgraph.nodes[key]["instance"] for key in component_task_keys], parent=self
|
|
1601
1652
|
)
|
|
@@ -2053,6 +2104,26 @@ class PipelineGraph:
|
|
|
2053
2104
|
"""
|
|
2054
2105
|
return self._xgraph.edge_subgraph([edge.key for edge in self.iter_edges(init)])
|
|
2055
2106
|
|
|
2107
|
+
def _make_task_xgraph_internal(self, init: bool) -> networkx.DiGraph:
|
|
2108
|
+
"""Make a init-only or runtime-only internal task subgraph.
|
|
2109
|
+
|
|
2110
|
+
See `make_task_xgraph` for parameters and return values.
|
|
2111
|
+
|
|
2112
|
+
Notes
|
|
2113
|
+
-----
|
|
2114
|
+
This method returns a view of the `PipelineGraph` object's internal
|
|
2115
|
+
backing graph, and hence should only be called in methods that copy the
|
|
2116
|
+
result either explicitly or by running a copying algorithm before
|
|
2117
|
+
returning it to the user.
|
|
2118
|
+
"""
|
|
2119
|
+
bipartite_xgraph = self._make_bipartite_xgraph_internal(init=init)
|
|
2120
|
+
task_keys = [
|
|
2121
|
+
key
|
|
2122
|
+
for key, bipartite in bipartite_xgraph.nodes(data="bipartite")
|
|
2123
|
+
if bipartite == NodeType.TASK.bipartite
|
|
2124
|
+
]
|
|
2125
|
+
return networkx.algorithms.bipartite.projected_graph(networkx.DiGraph(bipartite_xgraph), task_keys)
|
|
2126
|
+
|
|
2056
2127
|
def _transform_xgraph_state(self, xgraph: _G, skip_edges: bool) -> _G:
|
|
2057
2128
|
"""Transform networkx graph attributes in-place from the internal
|
|
2058
2129
|
"instance" attributes to the documented exported attributes.
|
|
@@ -2342,6 +2413,284 @@ class PipelineGraph:
|
|
|
2342
2413
|
f"{step_label!r}."
|
|
2343
2414
|
)
|
|
2344
2415
|
|
|
2416
|
+
def _select_expression(self, expr_tree: expressions.Node, task_xgraph: networkx.DiGraph) -> set[NodeKey]:
|
|
2417
|
+
"""Select tasks from a pipeline based on a string expression.
|
|
2418
|
+
|
|
2419
|
+
This is the primary implementation method for `select` and
|
|
2420
|
+
`select_tasks`.
|
|
2421
|
+
|
|
2422
|
+
Parameters
|
|
2423
|
+
----------
|
|
2424
|
+
expr_tree : `expressions.Node`
|
|
2425
|
+
Expression [sub]tree to process (recursively).
|
|
2426
|
+
task_xgraph : `networkx.DiGraph`
|
|
2427
|
+
NetworkX graph of all tasks (runtime nodes only) in the pipeline.
|
|
2428
|
+
|
|
2429
|
+
Returns
|
|
2430
|
+
-------
|
|
2431
|
+
selected : `set` [ `NodeKey` ]
|
|
2432
|
+
Set of `NodeKey` objects for matching tasks (only; no dataset type
|
|
2433
|
+
or task-init nodes).
|
|
2434
|
+
"""
|
|
2435
|
+
match expr_tree:
|
|
2436
|
+
case expressions.IdentifierNode(qualifier=qualifier, label=label):
|
|
2437
|
+
match self._select_identifier(qualifier, label):
|
|
2438
|
+
case NodeKey(node_type=NodeType.TASK) as task_key:
|
|
2439
|
+
return {task_key}
|
|
2440
|
+
case NodeKey(node_type=NodeType.DATASET_TYPE) as dataset_type_key:
|
|
2441
|
+
# Since a dataset type can have only one producer, this
|
|
2442
|
+
# yields 0- (for overall inputs) or 1-element sets.
|
|
2443
|
+
for producer_key, _ in self._xgraph.in_edges(dataset_type_key):
|
|
2444
|
+
if producer_key.node_type is NodeType.TASK_INIT:
|
|
2445
|
+
raise InvalidExpressionError(
|
|
2446
|
+
f"Init-output dataset type {label!r} cannot be used directly in an "
|
|
2447
|
+
"expression."
|
|
2448
|
+
)
|
|
2449
|
+
return {producer_key}
|
|
2450
|
+
return set()
|
|
2451
|
+
case TaskSubset() as task_subset:
|
|
2452
|
+
return {NodeKey(NodeType.TASK, label) for label in task_subset}
|
|
2453
|
+
case _: # pragma: no cover
|
|
2454
|
+
raise AssertionError("Identifier type inconsistent with grammar.")
|
|
2455
|
+
case expressions.DirectionNode(operator=operator, start=start):
|
|
2456
|
+
match self._select_identifier(start.qualifier, start.label):
|
|
2457
|
+
case NodeKey(node_type=NodeType.TASK) as task_key:
|
|
2458
|
+
if operator.startswith("<"):
|
|
2459
|
+
return self._select_task_ancestors(
|
|
2460
|
+
task_key, task_xgraph, inclusive=operator.endswith("=")
|
|
2461
|
+
)
|
|
2462
|
+
else:
|
|
2463
|
+
assert operator.startswith(">"), "Guaranteed by grammar."
|
|
2464
|
+
return self._select_task_descendants(
|
|
2465
|
+
task_key, task_xgraph, inclusive=operator.endswith("=")
|
|
2466
|
+
)
|
|
2467
|
+
case NodeKey(node_type=NodeType.DATASET_TYPE) as dataset_type_key:
|
|
2468
|
+
if operator.startswith("<"):
|
|
2469
|
+
return self._select_dataset_type_ancestors(
|
|
2470
|
+
dataset_type_key, task_xgraph, inclusive=operator.endswith("=")
|
|
2471
|
+
)
|
|
2472
|
+
else:
|
|
2473
|
+
assert operator.startswith(">"), "Guaranteed by grammar."
|
|
2474
|
+
return self._select_dataset_type_descendants(
|
|
2475
|
+
dataset_type_key, task_xgraph, inclusive=operator.endswith("=")
|
|
2476
|
+
)
|
|
2477
|
+
case TaskSubset():
|
|
2478
|
+
raise InvalidExpressionError(
|
|
2479
|
+
f"Task subset identifier {start!r} cannot be used as the start of an "
|
|
2480
|
+
"ancestor/descendant search."
|
|
2481
|
+
)
|
|
2482
|
+
case _: # pragma: no cover
|
|
2483
|
+
raise AssertionError("Unexpected parsed identifier result type.")
|
|
2484
|
+
case expressions.NotNode(operand=operand):
|
|
2485
|
+
operand_result = self._select_expression(operand, task_xgraph)
|
|
2486
|
+
return set(task_xgraph.nodes.keys() - operand_result)
|
|
2487
|
+
case expressions.UnionNode(lhs=lhs, rhs=rhs):
|
|
2488
|
+
lhs_result = self._select_expression(lhs, task_xgraph)
|
|
2489
|
+
rhs_result = self._select_expression(rhs, task_xgraph)
|
|
2490
|
+
return lhs_result.union(rhs_result)
|
|
2491
|
+
case expressions.IntersectionNode(lhs=lhs, rhs=rhs):
|
|
2492
|
+
lhs_result = self._select_expression(lhs, task_xgraph)
|
|
2493
|
+
rhs_result = self._select_expression(rhs, task_xgraph)
|
|
2494
|
+
return lhs_result.intersection(rhs_result)
|
|
2495
|
+
case _: # pragma: no cover
|
|
2496
|
+
raise AssertionError("Expression parse node inconsistent with grammar.")
|
|
2497
|
+
|
|
2498
|
+
def _select_task_ancestors(
|
|
2499
|
+
self, start: NodeKey, task_xgraph: networkx.DiGraph, inclusive: bool
|
|
2500
|
+
) -> set[NodeKey]:
|
|
2501
|
+
"""Return all task-node ancestors of the given task node, as defined by
|
|
2502
|
+
the `select` expression language.
|
|
2503
|
+
|
|
2504
|
+
Parameters
|
|
2505
|
+
----------
|
|
2506
|
+
start : `NodeKey`
|
|
2507
|
+
A runtime task node key.
|
|
2508
|
+
task_xgraph : `networkx.DiGraph`
|
|
2509
|
+
NetworkX graph of all tasks (runtime nodes only) in the pipeline.
|
|
2510
|
+
inclusive : `bool`
|
|
2511
|
+
Whether to include the ``start`` node in the results.
|
|
2512
|
+
|
|
2513
|
+
Returns
|
|
2514
|
+
-------
|
|
2515
|
+
selected : `set` [ `NodeKey` ]
|
|
2516
|
+
Set of `NodeKey` objects for matching tasks (only; no dataset type
|
|
2517
|
+
or task-init nodes).
|
|
2518
|
+
"""
|
|
2519
|
+
result = set(networkx.dag.ancestors(task_xgraph, start))
|
|
2520
|
+
if inclusive:
|
|
2521
|
+
result.add(start)
|
|
2522
|
+
return result
|
|
2523
|
+
|
|
2524
|
+
def _select_task_descendants(
|
|
2525
|
+
self, start: NodeKey, task_xgraph: networkx.DiGraph, inclusive: bool
|
|
2526
|
+
) -> set[NodeKey]:
|
|
2527
|
+
"""Return all task-node descendants of the given task node, as defined
|
|
2528
|
+
by the `select` expression language.
|
|
2529
|
+
|
|
2530
|
+
Parameters
|
|
2531
|
+
----------
|
|
2532
|
+
start : `NodeKey`
|
|
2533
|
+
A runtime task node key.
|
|
2534
|
+
task_xgraph : `networkx.DiGraph`
|
|
2535
|
+
NetworkX graph of all tasks (runtime nodes only) in the pipeline.
|
|
2536
|
+
inclusive : `bool`
|
|
2537
|
+
Whether to include the ``start`` node in the results.
|
|
2538
|
+
|
|
2539
|
+
Returns
|
|
2540
|
+
-------
|
|
2541
|
+
selected : `set` [ `NodeKey` ]
|
|
2542
|
+
Set of `NodeKey` objects for matching tasks (only; no dataset type
|
|
2543
|
+
or task-init nodes).
|
|
2544
|
+
"""
|
|
2545
|
+
result = set(networkx.dag.descendants(task_xgraph, start))
|
|
2546
|
+
if inclusive:
|
|
2547
|
+
result.add(start)
|
|
2548
|
+
return result
|
|
2549
|
+
|
|
2550
|
+
def _select_dataset_type_ancestors(
|
|
2551
|
+
self, start: NodeKey, task_xgraph: networkx.DiGraph, inclusive: bool
|
|
2552
|
+
) -> set[NodeKey]:
|
|
2553
|
+
"""Return all task-node ancestors of the given dataset type node, as
|
|
2554
|
+
defined by the `select` expression language.
|
|
2555
|
+
|
|
2556
|
+
Parameters
|
|
2557
|
+
----------
|
|
2558
|
+
start : `NodeKey`
|
|
2559
|
+
A dataset type node key. May not be an init-output.
|
|
2560
|
+
task_xgraph : `networkx.DiGraph`
|
|
2561
|
+
NetworkX graph of all tasks (runtime nodes only) in the pipeline.
|
|
2562
|
+
inclusive : `bool`
|
|
2563
|
+
Whether to include the producer of the ``start`` node in the
|
|
2564
|
+
results.
|
|
2565
|
+
|
|
2566
|
+
Returns
|
|
2567
|
+
-------
|
|
2568
|
+
selected : `set` [ `NodeKey` ]
|
|
2569
|
+
Set of `NodeKey` objects for matching tasks (only; no dataset type
|
|
2570
|
+
or task-init nodes).
|
|
2571
|
+
"""
|
|
2572
|
+
result: set[NodeKey] = set()
|
|
2573
|
+
for producer_key, _ in self._xgraph.in_edges(start):
|
|
2574
|
+
if producer_key.node_type is NodeType.TASK_INIT:
|
|
2575
|
+
raise InvalidExpressionError(
|
|
2576
|
+
f"Init-output dataset type {start.name!r} cannot be used as the "
|
|
2577
|
+
"starting point for an ancestor ('<' or '<=') search."
|
|
2578
|
+
)
|
|
2579
|
+
result.update(networkx.dag.ancestors(task_xgraph, producer_key))
|
|
2580
|
+
if inclusive:
|
|
2581
|
+
result.add(producer_key)
|
|
2582
|
+
return result
|
|
2583
|
+
|
|
2584
|
+
def _select_dataset_type_descendants(
|
|
2585
|
+
self, start: NodeKey, task_xgraph: networkx.DiGraph, inclusive: bool
|
|
2586
|
+
) -> set[NodeKey]:
|
|
2587
|
+
"""Return all task-node descendatns of the given dataset type node, as
|
|
2588
|
+
defined by the `select` expression language.
|
|
2589
|
+
|
|
2590
|
+
Parameters
|
|
2591
|
+
----------
|
|
2592
|
+
start : `NodeKey`
|
|
2593
|
+
A dataset type node key. May not be an init-output if
|
|
2594
|
+
``inclusive=True``.
|
|
2595
|
+
task_xgraph : `networkx.DiGraph`
|
|
2596
|
+
NetworkX graph of all tasks (runtime nodes only) in the pipeline.
|
|
2597
|
+
inclusive : `bool`
|
|
2598
|
+
Whether to include the producer of the ``start`` node in the
|
|
2599
|
+
results.
|
|
2600
|
+
|
|
2601
|
+
Returns
|
|
2602
|
+
-------
|
|
2603
|
+
selected : `set` [ `NodeKey` ]
|
|
2604
|
+
Set of `NodeKey` objects for matching tasks (only; no dataset type
|
|
2605
|
+
or task-init nodes).
|
|
2606
|
+
"""
|
|
2607
|
+
result: set[NodeKey] = set()
|
|
2608
|
+
if inclusive:
|
|
2609
|
+
for producer_key, _ in self._xgraph.in_edges(start):
|
|
2610
|
+
if producer_key.node_type is NodeType.TASK_INIT:
|
|
2611
|
+
raise InvalidExpressionError(
|
|
2612
|
+
f"Init-output dataset type {start.name!r} cannot be used as the "
|
|
2613
|
+
"starting point for an includsive descendant ('>=') search."
|
|
2614
|
+
)
|
|
2615
|
+
result.add(producer_key)
|
|
2616
|
+
# We also include tasks that consume a dataset type as an init-input,
|
|
2617
|
+
# since that can affect their runtime behavior.
|
|
2618
|
+
consumer_keys: set[NodeKey] = {
|
|
2619
|
+
(
|
|
2620
|
+
consumer_key
|
|
2621
|
+
if consumer_key.node_type is NodeType.TASK
|
|
2622
|
+
else NodeKey(NodeType.TASK, consumer_key.name)
|
|
2623
|
+
)
|
|
2624
|
+
for _, consumer_key in self._xgraph.out_edges(start)
|
|
2625
|
+
}
|
|
2626
|
+
for consumer_key in consumer_keys:
|
|
2627
|
+
result.add(consumer_key)
|
|
2628
|
+
result.update(networkx.dag.descendants(task_xgraph, consumer_key))
|
|
2629
|
+
return result
|
|
2630
|
+
|
|
2631
|
+
def _select_identifier(
|
|
2632
|
+
self, qualifier: Literal["T", "D", "S"] | None, label: str
|
|
2633
|
+
) -> NodeKey | TaskSubset:
|
|
2634
|
+
"""Return the node key or task subset that corresponds to a `select`
|
|
2635
|
+
expression identifier.
|
|
2636
|
+
|
|
2637
|
+
Parameters
|
|
2638
|
+
----------
|
|
2639
|
+
qualifier : `str` or `None`
|
|
2640
|
+
Task, dataset type, or task subset qualifier included in the
|
|
2641
|
+
identifier, if any.
|
|
2642
|
+
label : `str`
|
|
2643
|
+
Task label, dataset type name, or task subset label.
|
|
2644
|
+
|
|
2645
|
+
Returns
|
|
2646
|
+
-------
|
|
2647
|
+
key_or_subset : `NodeKey` or `TaskSubset`
|
|
2648
|
+
A `NodeKey` for a task or dataset type, or a `TaskSubset` for a
|
|
2649
|
+
task subset.
|
|
2650
|
+
"""
|
|
2651
|
+
match qualifier:
|
|
2652
|
+
case None:
|
|
2653
|
+
task_key = NodeKey(NodeType.TASK, label)
|
|
2654
|
+
dataset_type_key = NodeKey(NodeType.DATASET_TYPE, label)
|
|
2655
|
+
if task_key in self._xgraph.nodes:
|
|
2656
|
+
if dataset_type_key in self._xgraph.nodes:
|
|
2657
|
+
raise InvalidExpressionError(
|
|
2658
|
+
f"{label!r} is both a task label and a dataset type name; "
|
|
2659
|
+
"prefix with 'T:' or 'D:' (respectively) to specify which."
|
|
2660
|
+
)
|
|
2661
|
+
assert label not in self._task_subsets, "Should be prohibited at construction."
|
|
2662
|
+
return task_key
|
|
2663
|
+
elif dataset_type_key in self._xgraph.nodes:
|
|
2664
|
+
if label in self._task_subsets:
|
|
2665
|
+
raise InvalidExpressionError(
|
|
2666
|
+
f"{label!r} is both a subset label and a dataset type name; "
|
|
2667
|
+
"prefix with 'S:' or 'D:' (respectively) to specify which."
|
|
2668
|
+
)
|
|
2669
|
+
return dataset_type_key
|
|
2670
|
+
elif label in self._task_subsets:
|
|
2671
|
+
return self._task_subsets[label]
|
|
2672
|
+
else:
|
|
2673
|
+
raise InvalidExpressionError(
|
|
2674
|
+
f"{label!r} is not a task label, task subset label, or dataset type name."
|
|
2675
|
+
)
|
|
2676
|
+
case "T":
|
|
2677
|
+
task_key = NodeKey(NodeType.TASK, label)
|
|
2678
|
+
if task_key not in self._xgraph.nodes:
|
|
2679
|
+
raise InvalidExpressionError(f"Task with label {label!r} does not exist.")
|
|
2680
|
+
return task_key
|
|
2681
|
+
case "D":
|
|
2682
|
+
dataset_type_key = NodeKey(NodeType.DATASET_TYPE, label)
|
|
2683
|
+
if dataset_type_key not in self._xgraph.nodes:
|
|
2684
|
+
raise InvalidExpressionError(f"Dataset type with name {label!r} does not exist.")
|
|
2685
|
+
return dataset_type_key
|
|
2686
|
+
case "S":
|
|
2687
|
+
try:
|
|
2688
|
+
return self._task_subsets[label]
|
|
2689
|
+
except KeyError:
|
|
2690
|
+
raise InvalidExpressionError(f"Task subset with label {label!r} does not exist.")
|
|
2691
|
+
case _: # pragma: no cover
|
|
2692
|
+
raise AssertionError("Unexpected identifier qualifier in expression.")
|
|
2693
|
+
|
|
2345
2694
|
_xgraph: networkx.MultiDiGraph
|
|
2346
2695
|
_sorted_keys: Sequence[NodeKey] | None
|
|
2347
2696
|
_task_subsets: dict[str, TaskSubset]
|