Coverage for python / lsst / images / serialization / _input_archive.py: 100%
28 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-28 09:01 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-28 09:01 +0000
1# This file is part of lsst-images.
2#
3# Developed for the LSST Data Management System.
4# This product includes software developed by the LSST Project
5# (https://www.lsst.org).
6# See the COPYRIGHT file at the top-level directory of this distribution
7# for details of code ownership.
8#
9# Use of this source code is governed by a 3-clause BSD-style
10# license that can be found in the LICENSE file.
12from __future__ import annotations
14__all__ = ("InputArchive",)
16from abc import ABC, abstractmethod
17from collections.abc import Callable
18from types import EllipsisType
19from typing import TYPE_CHECKING, TypeVar
21import astropy.io.fits
22import astropy.table
23import astropy.units
24import numpy as np
25import pydantic
27from ._asdf_utils import ArrayReferenceModel
28from ._common import ArchiveTree, OpaqueArchiveMetadata, no_header_updates
29from ._tables import TableReferenceModel
31if TYPE_CHECKING:
32 from .._transforms import FrameSet
35# This pre-python-3.12 declaration is needed by Sphinx (probably the
36# autodoc-typehints plugin.
37P = TypeVar("P", bound=pydantic.BaseModel)
40class InputArchive[P: pydantic.BaseModel](ABC):
41 """Abstract interface for reading from a file format.
43 Notes
44 -----
45 An input archive instance is assumed to be paired with a Pydantic model
46 that represents a JSON tree, with the archive used to deserialize data that
47 is not native JSON from data that is (which may just be a reference to
48 binary data stored elsewhere in the file). The archive doesn't actually
49 hold that model instance because we'd prefer to avoid making the input
50 archive generic over the model type. It is expected that most concrete
51 archive implementations will provide a method to load the paired model from
52 a file, but this is not part of the base class interface.
53 """
55 @abstractmethod
56 def deserialize_pointer[U: ArchiveTree, V](
57 self, pointer: P, model_type: type[U], deserializer: Callable[[U, InputArchive[P]], V]
58 ) -> V:
59 """Deserialize an object that was saved by
60 `~lsst.serialization.OutputArchive.serialize_pointer`.
62 Parameters
63 ----------
64 pointer
65 JSON Pointer model to dereference.
66 model_type
67 Pydantic model type that the pointer should dereference to.
68 deserializer
69 Callable that takes an instance of ``model_type`` and an input
70 archive, and returns the deserialized object.
72 Returns
73 -------
74 V
75 The deserialized object.
77 Notes
78 -----
79 Implementations are required to remember previously-deserialized
80 objects and return them when the same pointer is passed in multiple
81 times.
83 There is no ``deserialize_direct`` (to pair with
84 `~lsst.serialization.OutputArchive.serialize_direct`) because the
85 caller can just call a deserializer function directly on a sub-model
86 of its Pydantic tree.
87 """
88 raise NotImplementedError()
90 @abstractmethod
91 def get_frame_set(self, ref: P) -> FrameSet:
92 """Return an already-deserialized frame set from the archive.
94 Parameters
95 ----------
96 ref
97 Implementation-specific reference to the frame set.
99 Returns
100 -------
101 FrameSet
102 Loaded frame set.
103 """
104 raise NotImplementedError()
106 @abstractmethod
107 def get_array(
108 self,
109 ref: ArrayReferenceModel,
110 *,
111 slices: tuple[slice, ...] | EllipsisType = ...,
112 strip_header: Callable[[astropy.io.fits.Header], None] = no_header_updates,
113 ) -> np.ndarray:
114 """Load an array from the archive.
116 Parameters
117 ----------
118 ref
119 A Pydantic model that references the array.
120 slices
121 Slices that specify a subset of the original array to read.
122 strip_header
123 A callable that strips out any FITS header cards added by the
124 ``update_header`` argument in the corresponding call to
125 `~lsst.images.serialization.OutputArchive.add_array`.
126 """
127 raise NotImplementedError()
129 @abstractmethod
130 def get_table(
131 self,
132 ref: TableReferenceModel,
133 strip_header: Callable[[astropy.io.fits.Header], None] = no_header_updates,
134 ) -> astropy.table.Table:
135 """Load a table from the archive.
137 Parameters
138 ----------
139 ref
140 A Pydantic model that references the table.
141 strip_header
142 A callable that strips out any FITS header cards added by the
143 ``update_header`` argument in the corresponding call to
144 `~lsst.serialization.OutputArchive.add_table`.
146 Returns
147 -------
148 astropy.table.Table
149 The loaded table.
150 """
151 raise NotImplementedError()
153 @abstractmethod
154 def get_structured_array(
155 self,
156 ref: TableReferenceModel,
157 strip_header: Callable[[astropy.io.fits.Header], None] = no_header_updates,
158 ) -> np.ndarray:
159 """Load a table from the archive as a structured array.
161 Parameters
162 ----------
163 ref
164 A Pydantic model that references the table.
165 strip_header
166 A callable that strips out any FITS header cards added by the
167 ``update_header`` argument in the corresponding call to
168 `~lsst.serialization.OutputArchive.add_structured_array`.
170 Returns
171 -------
172 numpy.ndarray
173 The loaded table as a structured array.
174 """
175 raise NotImplementedError()
177 @abstractmethod
178 def get_opaque_metadata(self) -> OpaqueArchiveMetadata:
179 """Return opaque metadata loaded from the file that should be saved if
180 another version of the object is saved to the same file format.
182 Returns
183 -------
184 OpaqueArchiveMetadata
185 Opaque metadata specific to this archive type that should be
186 round-tripped if it is saved in the same format.
187 """
188 raise NotImplementedError()