lsst-pipe-base 29.2025.4000__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.4000/python/lsst_pipe_base.egg-info → lsst_pipe_base-29.2025.4100}/PKG-INFO +1 -1
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_task_metadata.py +15 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/_versionDeserializers.py +6 -5
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/graph.py +2 -1
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/graphSummary.py +30 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/io.py +1 -1
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph/_common.py +23 -1
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph/_multiblock.py +174 -103
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph/_predicted.py +82 -6
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph_builder.py +4 -4
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_reports.py +45 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/_storage_class.py +45 -0
- lsst_pipe_base-29.2025.4100/python/lsst/pipe/base/version.py +2 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100/python/lsst_pipe_base.egg-info}/PKG-INFO +1 -1
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_pipeline_graph.py +5 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_predicted_qg.py +4 -4
- lsst_pipe_base-29.2025.4000/python/lsst/pipe/base/version.py +0 -2
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/COPYRIGHT +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/LICENSE +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/MANIFEST.in +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/README.md +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/bsd_license.txt +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/CHANGES.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/creating-a-pipeline.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/creating-a-pipelinetask.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/creating-a-task.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/index.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/task-framework-overview.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/task-retargeting-howto.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/testing-a-pipeline-task.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/testing-pipelines-with-mocks.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/doc/lsst.pipe.base/working-with-pipeline-graphs.rst +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/gpl-v3.0.txt +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/pyproject.toml +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_datasetQueryConstraints.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_dataset_handle.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_instrument.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_observation_dimension_packer.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_quantumContext.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/_status.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/all_dimensions_quantum_graph_builder.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/automatic_connection_constants.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/caching_limited_butler.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/_get_cli_subcommands.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/cmd/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/cmd/commands.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/opt/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/opt/arguments.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/cli/opt/options.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/config.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/configOverrides.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/connectionTypes.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/connections.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/dot_tools.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/exec_fixup_data_id.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/execution_graph_fixup.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/execution_reports.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/formatters/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/formatters/pexConfig.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/_implDetails.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/_loadHelpers.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph/quantumNode.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/graph_walker.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/log_capture.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/mermaid_tools.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/mp_graph_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipelineIR.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipelineTask.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/__main__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_dataset_types.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_edges.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_exceptions.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_mapping_views.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_nodes.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_pipeline_graph.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_task_subsets.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/_tasks.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/expressions.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_dot.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_formatting.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_layout.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_merge.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_mermaid.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_options.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_printer.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_show.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/pipeline_graph/visualization/_status_annotator.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/prerequisite_helpers.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/py.typed +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph/visualization.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_graph_skeleton.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/quantum_provenance_graph.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/register_instrument.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/retrieve_artifacts_for_quanta.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/transfer_from_graph.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/utils.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/script/zip_from_graph.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/separable_pipeline_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/simple_pipeline_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/single_quantum_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/struct.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/task.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/taskFactory.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/testUtils.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/in_memory_limited_butler.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/__init__.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/_data_id_match.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/_pipeline_task.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/mocks/_repo.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/no_dimensions.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/pipelineStepTester.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/simpleQGraph.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/tests/util.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst/pipe/base/utils.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/SOURCES.txt +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/dependency_links.txt +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/entry_points.txt +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/requires.txt +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/top_level.txt +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/python/lsst_pipe_base.egg-info/zip-safe +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/setup.cfg +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_adjust_all_quanta.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_caching_limited_butler.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_cliCmdRegisterInstrument.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_configOverrides.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_config_formatter.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_connections.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_dataid_match.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_dataset_handle.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_dot_tools.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_dynamic_connections.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_execution_reports.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_execution_storage_class_conversion.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_graphBuilder.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_graph_walker.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_init_output_run.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_instrument.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_mermaid.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_mp_graph_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_pipeline.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_pipelineIR.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_pipelineLoadSubset.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_pipelineTask.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_pipeline_graph_expressions.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_qg_builder_dimensions.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_quantumGraph.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_quantum_provenance_graph.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_quantum_reports.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_quantum_success_caveats.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_script_utils.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_separable_pipeline_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_simple_pipeline_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_single_quantum_executor.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_struct.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_task.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_task_factory.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_taskmetadata.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_testUtils.py +0 -0
- {lsst_pipe_base-29.2025.4000 → lsst_pipe_base-29.2025.4100}/tests/test_utils.py +0 -0
{lsst_pipe_base-29.2025.4000/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
|
{lsst_pipe_base-29.2025.4000 → 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()
|
|
@@ -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.4000 → 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()
|
|
@@ -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)
|
|
@@ -650,7 +650,7 @@ class SerializedTaskSubset(pydantic.BaseModel):
|
|
|
650
650
|
"""
|
|
651
651
|
members = set(self.tasks)
|
|
652
652
|
if label in steps:
|
|
653
|
-
steps.
|
|
653
|
+
steps._dimensions_by_label[label] = frozenset(self.dimensions)
|
|
654
654
|
return TaskSubset(xgraph, label, members, self.description, steps)
|
|
655
655
|
|
|
656
656
|
|
|
@@ -229,6 +229,21 @@ class HeaderModel(pydantic.BaseModel):
|
|
|
229
229
|
"""See `pydantic.BaseModel.model_json_schema`."""
|
|
230
230
|
return super().model_json_schema(*args, **kwargs)
|
|
231
231
|
|
|
232
|
+
@classmethod
|
|
233
|
+
def model_validate(cls, *args: Any, **kwargs: Any) -> Any:
|
|
234
|
+
"""See `pydantic.BaseModel.model_validate`."""
|
|
235
|
+
return super().model_validate(*args, **kwargs)
|
|
236
|
+
|
|
237
|
+
@classmethod
|
|
238
|
+
def model_validate_json(cls, *args: Any, **kwargs: Any) -> Any:
|
|
239
|
+
"""See `pydantic.BaseModel.model_validate_json`."""
|
|
240
|
+
return super().model_validate_json(*args, **kwargs)
|
|
241
|
+
|
|
242
|
+
@classmethod
|
|
243
|
+
def model_validate_strings(cls, *args: Any, **kwargs: Any) -> Any:
|
|
244
|
+
"""See `pydantic.BaseModel.model_validate_strings`."""
|
|
245
|
+
return super().model_validate_strings(*args, **kwargs)
|
|
246
|
+
|
|
232
247
|
|
|
233
248
|
class QuantumInfo(TypedDict):
|
|
234
249
|
"""A typed dictionary that annotates the attributes of the NetworkX graph
|
|
@@ -493,6 +508,7 @@ class BaseQuantumGraphReader:
|
|
|
493
508
|
*,
|
|
494
509
|
address_filename: str,
|
|
495
510
|
graph_type: str,
|
|
511
|
+
n_addresses: int,
|
|
496
512
|
page_size: int | None = None,
|
|
497
513
|
import_mode: TaskImportMode = TaskImportMode.ASSUME_CONSISTENT_EDGES,
|
|
498
514
|
) -> Iterator[Self]:
|
|
@@ -506,6 +522,8 @@ class BaseQuantumGraphReader:
|
|
|
506
522
|
Base filename for the address file.
|
|
507
523
|
graph_type : `str`
|
|
508
524
|
Value to expect for `HeaderModel.graph_type`.
|
|
525
|
+
n_addresses : `int`
|
|
526
|
+
Number of addresses to expect per row in the address file.
|
|
509
527
|
page_size : `int`, optional
|
|
510
528
|
Approximate number of bytes to read at once from address files.
|
|
511
529
|
Note that this does not set a page size for *all* reads, but it
|
|
@@ -539,7 +557,11 @@ class BaseQuantumGraphReader:
|
|
|
539
557
|
)
|
|
540
558
|
pipeline_graph = serialized_pipeline_graph.deserialize(import_mode)
|
|
541
559
|
with AddressReader.open_in_zip(
|
|
542
|
-
zf,
|
|
560
|
+
zf,
|
|
561
|
+
address_filename,
|
|
562
|
+
page_size=page_size,
|
|
563
|
+
int_size=header.int_size,
|
|
564
|
+
n_addresses=n_addresses,
|
|
543
565
|
) as address_reader:
|
|
544
566
|
yield cls(
|
|
545
567
|
header=header,
|
|
@@ -40,14 +40,13 @@ __all__ = (
|
|
|
40
40
|
)
|
|
41
41
|
|
|
42
42
|
import dataclasses
|
|
43
|
-
import itertools
|
|
44
43
|
import logging
|
|
45
44
|
import uuid
|
|
46
45
|
from collections.abc import Iterator
|
|
47
46
|
from contextlib import contextmanager
|
|
48
47
|
from io import BufferedReader, BytesIO
|
|
49
48
|
from operator import attrgetter
|
|
50
|
-
from typing import IO, TYPE_CHECKING,
|
|
49
|
+
from typing import IO, TYPE_CHECKING, Protocol, TypeAlias, TypeVar
|
|
51
50
|
|
|
52
51
|
import pydantic
|
|
53
52
|
|
|
@@ -61,6 +60,11 @@ _LOG = logging.getLogger(__name__)
|
|
|
61
60
|
_T = TypeVar("_T", bound=pydantic.BaseModel)
|
|
62
61
|
|
|
63
62
|
|
|
63
|
+
UUID_int: TypeAlias = int
|
|
64
|
+
|
|
65
|
+
MAX_UUID_INT: UUID_int = 2**128
|
|
66
|
+
|
|
67
|
+
|
|
64
68
|
DEFAULT_PAGE_SIZE: int = 5_000_000
|
|
65
69
|
"""Default page size for reading chunks of quantum graph files.
|
|
66
70
|
|
|
@@ -255,13 +259,51 @@ class AddressWriter:
|
|
|
255
259
|
self.write(stream, int_size=int_size)
|
|
256
260
|
|
|
257
261
|
|
|
262
|
+
@dataclasses.dataclass
|
|
263
|
+
class AddressPage:
|
|
264
|
+
"""A page of addresses in the `AddressReader`."""
|
|
265
|
+
|
|
266
|
+
file_offset: int
|
|
267
|
+
"""Offset in bytes to this page from the beginning of the file."""
|
|
268
|
+
|
|
269
|
+
begin: int
|
|
270
|
+
"""Index of the first row in this page."""
|
|
271
|
+
|
|
272
|
+
n_rows: int
|
|
273
|
+
"""Number of rows in this page."""
|
|
274
|
+
|
|
275
|
+
read: bool = False
|
|
276
|
+
"""Whether this page has already been read."""
|
|
277
|
+
|
|
278
|
+
@property
|
|
279
|
+
def end(self) -> int:
|
|
280
|
+
"""One past the last row index in this page."""
|
|
281
|
+
return self.begin + self.n_rows
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
@dataclasses.dataclass
|
|
285
|
+
class PageBounds:
|
|
286
|
+
"""A page index and the UUID interval that page covers."""
|
|
287
|
+
|
|
288
|
+
page_index: int
|
|
289
|
+
"""Index into the page array."""
|
|
290
|
+
|
|
291
|
+
uuid_int_begin: UUID_int
|
|
292
|
+
"""Integer representation of the smallest UUID in this page."""
|
|
293
|
+
|
|
294
|
+
uuid_int_end: UUID_int
|
|
295
|
+
"""One larger than the integer representation of the largest UUID in this
|
|
296
|
+
page.
|
|
297
|
+
"""
|
|
298
|
+
|
|
299
|
+
def __str__(self) -> str:
|
|
300
|
+
return f"{self.page_index} [{self.uuid_int_begin:x}:{self.uuid_int_end:x}]"
|
|
301
|
+
|
|
302
|
+
|
|
258
303
|
@dataclasses.dataclass
|
|
259
304
|
class AddressReader:
|
|
260
305
|
"""A helper object for reading address files for multi-block files."""
|
|
261
306
|
|
|
262
|
-
MAX_UUID_INT: ClassVar[int] = 2**128
|
|
263
|
-
"""The maximum value of a UUID's integer form."""
|
|
264
|
-
|
|
265
307
|
stream: IO[bytes]
|
|
266
308
|
"""Stream to read from."""
|
|
267
309
|
|
|
@@ -269,30 +311,28 @@ class AddressReader:
|
|
|
269
311
|
"""Size of each integer in bytes."""
|
|
270
312
|
|
|
271
313
|
n_rows: int
|
|
272
|
-
"""Number of
|
|
314
|
+
"""Number of rows in the file."""
|
|
273
315
|
|
|
274
316
|
n_addresses: int
|
|
275
317
|
"""Number of addresses in each row."""
|
|
276
318
|
|
|
277
|
-
|
|
278
|
-
"""
|
|
319
|
+
rows_per_page: int
|
|
320
|
+
"""Number of addresses in each page."""
|
|
279
321
|
|
|
280
|
-
rows: dict[uuid.UUID, AddressRow]
|
|
322
|
+
rows: dict[uuid.UUID, AddressRow] = dataclasses.field(default_factory=dict)
|
|
281
323
|
"""Rows that have already been read."""
|
|
282
324
|
|
|
283
|
-
|
|
284
|
-
"""
|
|
285
|
-
|
|
286
|
-
unread_pages: dict[int, int]
|
|
287
|
-
"""Pages that have not yet been read, as a mapping from page index to the
|
|
288
|
-
number of rows in that page.
|
|
325
|
+
rows_by_index: dict[int, AddressRow] = dataclasses.field(default_factory=dict)
|
|
326
|
+
"""Rows that have already been read, keyed by integer index."""
|
|
289
327
|
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
"""
|
|
328
|
+
pages: list[AddressPage] = dataclasses.field(default_factory=list)
|
|
329
|
+
page_bounds: dict[int, PageBounds] = dataclasses.field(default_factory=dict)
|
|
330
|
+
"""Mapping from page index to page boundary information."""
|
|
293
331
|
|
|
294
332
|
@classmethod
|
|
295
|
-
def from_stream(
|
|
333
|
+
def from_stream(
|
|
334
|
+
cls, stream: IO[bytes], *, page_size: int, n_addresses: int, int_size: int
|
|
335
|
+
) -> AddressReader:
|
|
296
336
|
"""Construct from a stream by reading the header.
|
|
297
337
|
|
|
298
338
|
Parameters
|
|
@@ -302,27 +342,47 @@ class AddressReader:
|
|
|
302
342
|
page_size : `int`
|
|
303
343
|
Approximate number of bytes to read at a time when searching for an
|
|
304
344
|
address.
|
|
305
|
-
|
|
306
|
-
|
|
345
|
+
n_addresses : `int`
|
|
346
|
+
Number of addresses to expect per row. This is checked against
|
|
347
|
+
the size embedded in the file.
|
|
348
|
+
int_size : `int`
|
|
349
|
+
Number of bytes to use for all integers. This is checked against
|
|
350
|
+
the size embedded in the file.
|
|
307
351
|
"""
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
352
|
+
header_size = cls.compute_header_size(int_size)
|
|
353
|
+
row_size = cls.compute_row_size(int_size, n_addresses)
|
|
354
|
+
# Read the raw header page.
|
|
355
|
+
header_page_data = stream.read(header_size)
|
|
356
|
+
if len(header_page_data) < header_size:
|
|
357
|
+
raise InvalidQuantumGraphFileError("Address file unexpectedly truncated.")
|
|
358
|
+
# Interpret the raw header data and initialize the reader instance.
|
|
359
|
+
header_page_stream = BytesIO(header_page_data)
|
|
360
|
+
file_int_size = int.from_bytes(header_page_stream.read(1))
|
|
361
|
+
if file_int_size != int_size:
|
|
362
|
+
raise InvalidQuantumGraphFileError(
|
|
363
|
+
f"int size in address file ({file_int_size}) does not match int size in header ({int_size})."
|
|
364
|
+
)
|
|
365
|
+
n_rows = int.from_bytes(header_page_stream.read(int_size))
|
|
366
|
+
file_n_addresses = int.from_bytes(header_page_stream.read(int_size))
|
|
367
|
+
if file_n_addresses != n_addresses:
|
|
368
|
+
raise InvalidQuantumGraphFileError(
|
|
369
|
+
f"Incorrect number of addresses per row: expected {n_addresses}, got {file_n_addresses}."
|
|
370
|
+
)
|
|
371
|
+
rows_per_page = max(page_size // row_size, 1)
|
|
372
|
+
# Construct an instance.
|
|
373
|
+
self = cls(stream, int_size, n_rows, n_addresses, rows_per_page=rows_per_page)
|
|
374
|
+
# Calculate positions of each page of rows.
|
|
375
|
+
row_index = 0
|
|
376
|
+
file_offset = header_size
|
|
377
|
+
while row_index < n_rows:
|
|
378
|
+
self.pages.append(AddressPage(file_offset=file_offset, begin=row_index, n_rows=rows_per_page))
|
|
379
|
+
row_index += rows_per_page
|
|
380
|
+
file_offset += rows_per_page * row_size
|
|
381
|
+
if row_index != n_rows:
|
|
382
|
+
# Last page was too big.
|
|
383
|
+
self.pages[-1].n_rows -= row_index - n_rows
|
|
384
|
+
assert sum(p.n_rows for p in self.pages) == n_rows, "Bad logic setting page row counts."
|
|
385
|
+
return self
|
|
326
386
|
|
|
327
387
|
@classmethod
|
|
328
388
|
@contextmanager
|
|
@@ -330,9 +390,10 @@ class AddressReader:
|
|
|
330
390
|
cls,
|
|
331
391
|
zf: zipfile.ZipFile,
|
|
332
392
|
name: str,
|
|
393
|
+
*,
|
|
333
394
|
page_size: int,
|
|
334
|
-
|
|
335
|
-
|
|
395
|
+
n_addresses: int,
|
|
396
|
+
int_size: int,
|
|
336
397
|
) -> Iterator[AddressReader]:
|
|
337
398
|
"""Make a reader for an address file in a zip archive.
|
|
338
399
|
|
|
@@ -345,11 +406,12 @@ class AddressReader:
|
|
|
345
406
|
page_size : `int`
|
|
346
407
|
Approximate number of bytes to read at a time when searching for an
|
|
347
408
|
address.
|
|
348
|
-
|
|
409
|
+
n_addresses : `int`
|
|
410
|
+
Number of addresses to expect per row. This is checked against
|
|
411
|
+
the size embedded in the file.
|
|
412
|
+
int_size : `int`
|
|
349
413
|
Number of bytes to use for all integers. This is checked against
|
|
350
414
|
the size embedded in the file.
|
|
351
|
-
start_index : `int`, optional
|
|
352
|
-
Value of the first index in the file.
|
|
353
415
|
|
|
354
416
|
Returns
|
|
355
417
|
-------
|
|
@@ -357,12 +419,7 @@ class AddressReader:
|
|
|
357
419
|
Context manager that returns a reader when entered.
|
|
358
420
|
"""
|
|
359
421
|
with zf.open(f"{name}.addr", mode="r") as stream:
|
|
360
|
-
|
|
361
|
-
if int_size is not None and result.int_size != int_size:
|
|
362
|
-
raise InvalidQuantumGraphFileError(
|
|
363
|
-
"int size in address file does not match int size in header."
|
|
364
|
-
)
|
|
365
|
-
yield result
|
|
422
|
+
yield cls.from_stream(stream, page_size=page_size, n_addresses=n_addresses, int_size=int_size)
|
|
366
423
|
|
|
367
424
|
@staticmethod
|
|
368
425
|
def compute_header_size(int_size: int) -> int:
|
|
@@ -409,11 +466,6 @@ class AddressReader:
|
|
|
409
466
|
)
|
|
410
467
|
)
|
|
411
468
|
|
|
412
|
-
@property
|
|
413
|
-
def header_size(self) -> int:
|
|
414
|
-
"""The size (in bytes) of the header of this address file."""
|
|
415
|
-
return self.compute_header_size(self.int_size)
|
|
416
|
-
|
|
417
469
|
@property
|
|
418
470
|
def row_size(self) -> int:
|
|
419
471
|
"""The size (in bytes) of each row of this address file."""
|
|
@@ -427,16 +479,26 @@ class AddressReader:
|
|
|
427
479
|
rows : `dict` [ `uuid.UUID`, `AddressRow` ]
|
|
428
480
|
Mapping of loaded address rows, keyed by UUID.
|
|
429
481
|
"""
|
|
482
|
+
# Skip any pages from the beginning that have already been read; this
|
|
483
|
+
# nicely handles both the case where we already read everything (or
|
|
484
|
+
# there was nothing to read) while giving us a page with a file offset
|
|
485
|
+
# to start from.
|
|
486
|
+
for page in self.pages:
|
|
487
|
+
if not page.read:
|
|
488
|
+
break
|
|
489
|
+
else:
|
|
490
|
+
return self.rows
|
|
491
|
+
# Read the entire rest of the file into memory.
|
|
492
|
+
self.stream.seek(page.file_offset)
|
|
493
|
+
data = self.stream.read()
|
|
494
|
+
buffer = BytesIO(data)
|
|
430
495
|
# Shortcut out if we've already read everything, but don't bother
|
|
431
496
|
# optimizing previous partial reads.
|
|
432
|
-
|
|
433
|
-
self.
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
for _ in range(self.n_rows):
|
|
438
|
-
self._read_row(buffer)
|
|
439
|
-
self.unread_pages.clear()
|
|
497
|
+
while len(self.rows) < self.n_rows:
|
|
498
|
+
self._read_row(buffer)
|
|
499
|
+
# Delete all pages; they don't matter anymore, and that's easier than
|
|
500
|
+
# updating them to reflect the reads we've done.
|
|
501
|
+
self.pages.clear()
|
|
440
502
|
return self.rows
|
|
441
503
|
|
|
442
504
|
def find(self, key: uuid.UUID) -> AddressRow:
|
|
@@ -452,54 +514,63 @@ class AddressReader:
|
|
|
452
514
|
row : `AddressRow`
|
|
453
515
|
Addresses for the given UUID.
|
|
454
516
|
"""
|
|
455
|
-
|
|
517
|
+
match key:
|
|
518
|
+
case uuid.UUID():
|
|
519
|
+
return self._find_uuid(key)
|
|
520
|
+
case _:
|
|
521
|
+
raise TypeError(f"Invalid argument: {key}.")
|
|
522
|
+
|
|
523
|
+
def _find_uuid(self, target: uuid.UUID) -> AddressRow:
|
|
524
|
+
if (row := self.rows.get(target)) is not None:
|
|
456
525
|
return row
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
self.
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
526
|
+
if self.n_rows == 0 or not self.pages:
|
|
527
|
+
raise LookupError(f"Address for {target} not found.")
|
|
528
|
+
|
|
529
|
+
# Use a binary search to find the page containing the target UUID.
|
|
530
|
+
left = 0
|
|
531
|
+
right = len(self.pages) - 1
|
|
532
|
+
while left <= right:
|
|
533
|
+
mid = left + ((right - left) // 2)
|
|
534
|
+
self._read_page(mid)
|
|
535
|
+
if (row := self.rows.get(target)) is not None:
|
|
536
|
+
return row
|
|
537
|
+
bounds = self.page_bounds[mid]
|
|
538
|
+
if target.int < bounds.uuid_int_begin:
|
|
539
|
+
right = mid - 1
|
|
540
|
+
elif target.int > bounds.uuid_int_end:
|
|
541
|
+
left = mid + 1
|
|
542
|
+
else:
|
|
543
|
+
# Should have been on this page, but it wasn't.
|
|
544
|
+
raise LookupError(f"Address for {target} not found.")
|
|
545
|
+
|
|
546
|
+
# Ran out of pages to search.
|
|
547
|
+
raise LookupError(f"Address for {target} not found.")
|
|
548
|
+
|
|
549
|
+
def _read_page(self, page_index: int, page_stream: BytesIO | None = None) -> bool:
|
|
550
|
+
page = self.pages[page_index]
|
|
551
|
+
if page.read:
|
|
552
|
+
return False
|
|
553
|
+
if page_stream is None:
|
|
554
|
+
self.stream.seek(page.file_offset)
|
|
555
|
+
page_stream = BytesIO(self.stream.read(page.n_rows * self.row_size))
|
|
556
|
+
row = self._read_row(page_stream)
|
|
557
|
+
uuid_int_begin = row.key.int
|
|
558
|
+
for _ in range(1, page.n_rows):
|
|
559
|
+
row = self._read_row(page_stream)
|
|
560
|
+
uuid_int_end = row.key.int + 1 # Python's loop scoping rules are actually useful here!
|
|
561
|
+
page.read = True
|
|
562
|
+
bounds = PageBounds(page_index=page_index, uuid_int_begin=uuid_int_begin, uuid_int_end=uuid_int_end)
|
|
563
|
+
self.page_bounds[page_index] = bounds
|
|
564
|
+
_LOG.debug("Read page %s with rows [%s:%s].", bounds, page.begin, page.end)
|
|
565
|
+
return True
|
|
490
566
|
|
|
491
567
|
def _read_row(self, page_stream: BytesIO) -> AddressRow:
|
|
492
568
|
row = AddressRow.read(page_stream, self.n_addresses, self.int_size)
|
|
493
569
|
self.rows[row.key] = row
|
|
570
|
+
self.rows_by_index[row.index] = row
|
|
494
571
|
_LOG.debug("Read address row %s.", row)
|
|
495
572
|
return row
|
|
496
573
|
|
|
497
|
-
def _page_search_path(self, mid: int) -> Iterator[int]:
|
|
498
|
-
yield mid
|
|
499
|
-
for abs_offset in itertools.count(1):
|
|
500
|
-
yield mid + abs_offset
|
|
501
|
-
yield mid - abs_offset
|
|
502
|
-
|
|
503
574
|
|
|
504
575
|
@dataclasses.dataclass
|
|
505
576
|
class MultiblockWriter:
|