lsst-pipe-base 30.0.0rc3__py3-none-any.whl → 30.0.1rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. lsst/pipe/base/_instrument.py +25 -15
  2. lsst/pipe/base/_quantumContext.py +3 -3
  3. lsst/pipe/base/_status.py +43 -10
  4. lsst/pipe/base/_task_metadata.py +2 -2
  5. lsst/pipe/base/all_dimensions_quantum_graph_builder.py +8 -3
  6. lsst/pipe/base/automatic_connection_constants.py +20 -1
  7. lsst/pipe/base/cli/cmd/__init__.py +18 -2
  8. lsst/pipe/base/cli/cmd/commands.py +149 -4
  9. lsst/pipe/base/connectionTypes.py +72 -160
  10. lsst/pipe/base/connections.py +6 -9
  11. lsst/pipe/base/execution_reports.py +0 -5
  12. lsst/pipe/base/graph/graph.py +11 -10
  13. lsst/pipe/base/graph/quantumNode.py +4 -4
  14. lsst/pipe/base/graph_walker.py +8 -10
  15. lsst/pipe/base/log_capture.py +1 -1
  16. lsst/pipe/base/log_on_close.py +4 -7
  17. lsst/pipe/base/pipeline.py +5 -6
  18. lsst/pipe/base/pipelineIR.py +2 -8
  19. lsst/pipe/base/pipelineTask.py +5 -7
  20. lsst/pipe/base/pipeline_graph/_dataset_types.py +2 -2
  21. lsst/pipe/base/pipeline_graph/_edges.py +32 -22
  22. lsst/pipe/base/pipeline_graph/_mapping_views.py +4 -7
  23. lsst/pipe/base/pipeline_graph/_pipeline_graph.py +14 -7
  24. lsst/pipe/base/pipeline_graph/expressions.py +2 -2
  25. lsst/pipe/base/pipeline_graph/io.py +7 -10
  26. lsst/pipe/base/pipeline_graph/visualization/_dot.py +13 -12
  27. lsst/pipe/base/pipeline_graph/visualization/_layout.py +16 -18
  28. lsst/pipe/base/pipeline_graph/visualization/_merge.py +4 -7
  29. lsst/pipe/base/pipeline_graph/visualization/_printer.py +10 -10
  30. lsst/pipe/base/pipeline_graph/visualization/_status_annotator.py +7 -0
  31. lsst/pipe/base/prerequisite_helpers.py +2 -1
  32. lsst/pipe/base/quantum_graph/_common.py +15 -17
  33. lsst/pipe/base/quantum_graph/_multiblock.py +36 -20
  34. lsst/pipe/base/quantum_graph/_predicted.py +7 -3
  35. lsst/pipe/base/quantum_graph/_provenance.py +501 -61
  36. lsst/pipe/base/quantum_graph/aggregator/__init__.py +0 -1
  37. lsst/pipe/base/quantum_graph/aggregator/_communicators.py +187 -240
  38. lsst/pipe/base/quantum_graph/aggregator/_config.py +87 -9
  39. lsst/pipe/base/quantum_graph/aggregator/_ingester.py +13 -12
  40. lsst/pipe/base/quantum_graph/aggregator/_scanner.py +15 -7
  41. lsst/pipe/base/quantum_graph/aggregator/_structs.py +3 -3
  42. lsst/pipe/base/quantum_graph/aggregator/_supervisor.py +19 -34
  43. lsst/pipe/base/quantum_graph/aggregator/_workers.py +303 -0
  44. lsst/pipe/base/quantum_graph/aggregator/_writer.py +3 -3
  45. lsst/pipe/base/quantum_graph/formatter.py +74 -4
  46. lsst/pipe/base/quantum_graph/ingest_graph.py +413 -0
  47. lsst/pipe/base/quantum_graph/visualization.py +5 -1
  48. lsst/pipe/base/quantum_graph_builder.py +21 -8
  49. lsst/pipe/base/quantum_graph_skeleton.py +31 -29
  50. lsst/pipe/base/quantum_provenance_graph.py +29 -12
  51. lsst/pipe/base/separable_pipeline_executor.py +1 -1
  52. lsst/pipe/base/single_quantum_executor.py +15 -8
  53. lsst/pipe/base/struct.py +4 -0
  54. lsst/pipe/base/testUtils.py +3 -3
  55. lsst/pipe/base/tests/mocks/_storage_class.py +2 -1
  56. lsst/pipe/base/version.py +1 -1
  57. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/METADATA +3 -3
  58. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/RECORD +66 -64
  59. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/WHEEL +1 -1
  60. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/entry_points.txt +0 -0
  61. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/licenses/COPYRIGHT +0 -0
  62. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/licenses/LICENSE +0 -0
  63. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/licenses/bsd_license.txt +0 -0
  64. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/licenses/gpl-v3.0.txt +0 -0
  65. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/top_level.txt +0 -0
  66. {lsst_pipe_base-30.0.0rc3.dist-info → lsst_pipe_base-30.0.1rc1.dist-info}/zip-safe +0 -0
@@ -50,9 +50,7 @@ from typing import (
50
50
  TYPE_CHECKING,
51
51
  Any,
52
52
  Self,
53
- TypeAlias,
54
53
  TypedDict,
55
- TypeVar,
56
54
  )
57
55
 
58
56
  import networkx
@@ -81,18 +79,16 @@ if TYPE_CHECKING:
81
79
  # These aliases make it a lot easier how the various pydantic models are
82
80
  # structured, but they're too verbose to be worth exporting to code outside the
83
81
  # quantum_graph subpackage.
84
- TaskLabel: TypeAlias = str
85
- DatasetTypeName: TypeAlias = str
86
- ConnectionName: TypeAlias = str
87
- DatasetIndex: TypeAlias = int
88
- QuantumIndex: TypeAlias = int
89
- DatastoreName: TypeAlias = str
90
- DimensionElementName: TypeAlias = str
91
- DataCoordinateValues: TypeAlias = list[DataIdValue]
82
+ type TaskLabel = str
83
+ type DatasetTypeName = str
84
+ type ConnectionName = str
85
+ type DatasetIndex = int
86
+ type QuantumIndex = int
87
+ type DatastoreName = str
88
+ type DimensionElementName = str
89
+ type DataCoordinateValues = list[DataIdValue]
92
90
 
93
91
 
94
- _T = TypeVar("_T", bound=pydantic.BaseModel)
95
-
96
92
  FORMAT_VERSION: int = 1
97
93
  """
98
94
  File format version number for new files.
@@ -453,8 +449,10 @@ class BaseQuantumGraphWriter:
453
449
  cdict_data: bytes | None = None,
454
450
  zstd_level: int = 10,
455
451
  ) -> Iterator[Self]:
456
- uri = ResourcePath(uri)
452
+ uri = ResourcePath(uri, forceDirectory=False)
457
453
  address_writer = AddressWriter()
454
+ if uri.isLocal:
455
+ os.makedirs(uri.dirname().ospath, exist_ok=True)
458
456
  cdict = zstandard.ZstdCompressionDict(cdict_data) if cdict_data is not None else None
459
457
  compressor = zstandard.ZstdCompressor(level=zstd_level, dict_data=cdict)
460
458
  with uri.open(mode="wb") as stream:
@@ -595,9 +593,9 @@ class BaseQuantumGraphReader:
595
593
  )
596
594
 
597
595
  @staticmethod
598
- def _read_single_block_static(
599
- name: str, model_type: type[_T], zf: zipfile.ZipFile, decompressor: Decompressor
600
- ) -> _T:
596
+ def _read_single_block_static[T: pydantic.BaseModel](
597
+ name: str, model_type: type[T], zf: zipfile.ZipFile, decompressor: Decompressor
598
+ ) -> T:
601
599
  """Read a single compressed JSON block from a 'file' in a zip archive.
602
600
 
603
601
  Parameters
@@ -620,7 +618,7 @@ class BaseQuantumGraphReader:
620
618
  json_data = decompressor.decompress(compressed_data)
621
619
  return model_type.model_validate_json(json_data)
622
620
 
623
- def _read_single_block(self, name: str, model_type: type[_T]) -> _T:
621
+ def _read_single_block[T: pydantic.BaseModel](self, name: str, model_type: type[T]) -> T:
624
622
  """Read a single compressed JSON block from a 'file' in a zip archive.
625
623
 
626
624
  Parameters
@@ -43,25 +43,22 @@ import dataclasses
43
43
  import logging
44
44
  import tempfile
45
45
  import uuid
46
- from collections.abc import Iterator
46
+ import zipfile
47
+ from collections.abc import Iterator, Set
47
48
  from contextlib import contextmanager
48
49
  from io import BufferedReader, BytesIO
49
50
  from operator import attrgetter
50
- from typing import IO, TYPE_CHECKING, Protocol, TypeAlias, TypeVar
51
+ from typing import IO, Protocol, TypeVar
51
52
 
52
53
  import pydantic
53
54
 
54
- if TYPE_CHECKING:
55
- import zipfile
56
-
57
-
58
55
  _LOG = logging.getLogger(__name__)
59
56
 
60
57
 
61
58
  _T = TypeVar("_T", bound=pydantic.BaseModel)
62
59
 
63
60
 
64
- UUID_int: TypeAlias = int
61
+ type UUID_int = int
65
62
 
66
63
  MAX_UUID_INT: UUID_int = 2**128
67
64
 
@@ -77,7 +74,7 @@ individual quanta (especially for execution).
77
74
 
78
75
 
79
76
  class Compressor(Protocol):
80
- """A protocol for objects with a `compress` method that takes and returns
77
+ """A protocol for objects with a ``compress`` method that takes and returns
81
78
  `bytes`.
82
79
  """
83
80
 
@@ -208,11 +205,11 @@ class AddressWriter:
208
205
  addresses: list[dict[uuid.UUID, Address]] = dataclasses.field(default_factory=list)
209
206
  """Addresses to store with each UUID.
210
207
 
211
- Every key in one of these dictionaries must have an entry in `indices`.
208
+ Every key in one of these dictionaries must have an entry in ``indices``.
212
209
  The converse is not true.
213
210
  """
214
211
 
215
- def write(self, stream: IO[bytes], int_size: int) -> None:
212
+ def write(self, stream: IO[bytes], int_size: int, all_ids: Set[uuid.UUID] | None = None) -> None:
216
213
  """Write all addresses to a file-like object.
217
214
 
218
215
  Parameters
@@ -221,15 +218,17 @@ class AddressWriter:
221
218
  Binary file-like object.
222
219
  int_size : `int`
223
220
  Number of bytes to use for all integers.
221
+ all_ids : `~collections.abc.Set` [`uuid.UUID`], optional
222
+ Set of the union of all UUIDs in any dictionary from a call to
223
+ `get_all_ids`.
224
224
  """
225
- indices: set[uuid.UUID] = set()
226
- for address_map in self.addresses:
227
- indices.update(address_map.keys())
225
+ if all_ids is None:
226
+ all_ids = self.get_all_ids()
228
227
  stream.write(int_size.to_bytes(1))
229
- stream.write(len(indices).to_bytes(int_size))
228
+ stream.write(len(all_ids).to_bytes(int_size))
230
229
  stream.write(len(self.addresses).to_bytes(int_size))
231
230
  empty_address = Address()
232
- for n, key in enumerate(sorted(indices, key=attrgetter("int"))):
231
+ for n, key in enumerate(sorted(all_ids, key=attrgetter("int"))):
233
232
  row = AddressRow(key, n, [m.get(key, empty_address) for m in self.addresses])
234
233
  _LOG.debug("Wrote address %s.", row)
235
234
  row.write(stream, int_size)
@@ -246,8 +245,25 @@ class AddressWriter:
246
245
  int_size : `int`
247
246
  Number of bytes to use for all integers.
248
247
  """
249
- with zf.open(f"{name}.addr", mode="w") as stream:
250
- self.write(stream, int_size=int_size)
248
+ all_ids = self.get_all_ids()
249
+ zip_info = zipfile.ZipInfo(f"{name}.addr")
250
+ row_size = AddressReader.compute_row_size(int_size, len(self.addresses))
251
+ zip_info.file_size = AddressReader.compute_header_size(int_size) + len(all_ids) * row_size
252
+ with zf.open(zip_info, mode="w") as stream:
253
+ self.write(stream, int_size=int_size, all_ids=all_ids)
254
+
255
+ def get_all_ids(self) -> Set[uuid.UUID]:
256
+ """Return all IDs used by any address dictionary.
257
+
258
+ Returns
259
+ -------
260
+ all_ids : `~collections.abc.Set` [`uuid.UUID`]
261
+ Set of all IDs.
262
+ """
263
+ all_ids: set[uuid.UUID] = set()
264
+ for address_map in self.addresses:
265
+ all_ids.update(address_map.keys())
266
+ return all_ids
251
267
 
252
268
 
253
269
  @dataclasses.dataclass
@@ -646,7 +662,7 @@ class MultiblockWriter:
646
662
  model : `pydantic.BaseModel`
647
663
  Model to convert to JSON and compress.
648
664
  compressor : `Compressor`
649
- Object with a `compress` method that takes and returns `bytes`.
665
+ Object with a ``compress`` method that takes and returns `bytes`.
650
666
 
651
667
  Returns
652
668
  -------
@@ -743,7 +759,7 @@ class MultiblockReader:
743
759
  model_type : `type` [ `pydantic.BaseModel` ]
744
760
  Pydantic model to validate JSON with.
745
761
  decompressor : `Decompressor`
746
- Object with a `decompress` method that takes and returns `bytes`.
762
+ Object with a ``decompress`` method that takes and returns `bytes`.
747
763
  int_size : `int`
748
764
  Number of bytes to use for all integers.
749
765
  page_size : `int`
@@ -793,7 +809,7 @@ class MultiblockReader:
793
809
  model_type : `type` [ `pydantic.BaseModel` ]
794
810
  Pydantic model to validate JSON with.
795
811
  decompressor : `Decompressor`
796
- Object with a `decompress` method that takes and returns `bytes`.
812
+ Object with a ``decompress`` method that takes and returns `bytes`.
797
813
 
798
814
  Returns
799
815
  -------
@@ -49,7 +49,7 @@ import warnings
49
49
  from collections import defaultdict
50
50
  from collections.abc import Iterable, Iterator, Mapping, Sequence
51
51
  from contextlib import AbstractContextManager, contextmanager
52
- from typing import TYPE_CHECKING, Any, TypeVar, cast
52
+ from typing import TYPE_CHECKING, Any, cast
53
53
 
54
54
  import networkx
55
55
  import networkx.algorithms.bipartite
@@ -110,10 +110,14 @@ if TYPE_CHECKING:
110
110
  from ..config import PipelineTaskConfig
111
111
  from ..graph import QgraphSummary, QuantumGraph
112
112
 
113
- _LOG = logging.getLogger(__name__)
113
+ # Sphinx needs imports for type annotations of base class members.
114
+ if "sphinx" in sys.modules:
115
+ import zipfile # noqa: F401
116
+
117
+ from ._multiblock import AddressReader, Decompressor # noqa: F401
114
118
 
115
119
 
116
- _T = TypeVar("_T", bound=pydantic.BaseModel)
120
+ _LOG = logging.getLogger(__name__)
117
121
 
118
122
 
119
123
  class _PredictedThinQuantumModelV0(pydantic.BaseModel):