Coverage for python/lsst/daf/butler/remote_butler/_ref_utils.py: 0%

55 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-09-01 02:05 -0700

1# This file is part of daf_butler. 

2# 

3# Developed for the LSST Data Management System. 

4# This product includes software developed by the LSST Project 

5# (http://www.lsst.org). 

6# See the COPYRIGHT file at the top-level directory of this distribution 

7# for details of code ownership. 

8# 

9# This software is dual licensed under the GNU General Public License and also 

10# under a 3-clause BSD license. Recipients may choose which of these licenses 

11# to use; please see the files gpl-3.0.txt and/or bsd_license.txt, 

12# respectively. If you choose the GPL option then the following text applies 

13# (but note that there is still no warranty even if you opt for BSD instead): 

14# 

15# This program is free software: you can redistribute it and/or modify 

16# it under the terms of the GNU General Public License as published by 

17# the Free Software Foundation, either version 3 of the License, or 

18# (at your option) any later version. 

19# 

20# This program is distributed in the hope that it will be useful, 

21# but WITHOUT ANY WARRANTY; without even the implied warranty of 

22# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

23# GNU General Public License for more details. 

24# 

25# You should have received a copy of the GNU General Public License 

26# along with this program. If not, see <http://www.gnu.org/licenses/>. 

27 

28from __future__ import annotations 

29 

30__all__ = ( 

31 "apply_storage_class_override", 

32 "get_component_override", 

33 "make_read_ref", 

34 "normalize_dataset_type_name", 

35 "simplify_dataId", 

36 "split_dataset_type_name", 

37) 

38 

39from pydantic import TypeAdapter 

40 

41from .._dataset_ref import DatasetRef 

42from .._dataset_type import DatasetType, get_dataset_type_name, validate_dataset_type_name 

43from .._storage_class import StorageClass 

44from ..dimensions import DataCoordinate, DataId, DataIdValue, SerializedDataId 

45from .server_models import DatasetTypeName 

46 

47_SERIALIZED_DATA_ID_TYPE_ADAPTER = TypeAdapter(SerializedDataId) 

48 

49 

50def apply_storage_class_override( 

51 ref: DatasetRef, 

52 original_dataset_ref_or_type: DatasetRef | DatasetType | str, 

53 explicit_storage_class: StorageClass | str | None, 

54) -> DatasetRef: 

55 """Return a DatasetRef with its storage class overridden to match the 

56 StorageClass supplied by the user as input to one of the search functions. 

57 

58 Parameters 

59 ---------- 

60 ref : `DatasetRef` 

61 The ref to which we will apply the StorageClass override. 

62 original_dataset_ref_or_type : `DatasetRef` | `DatasetType` | `str` 

63 The ref or type that was input to the search, which may contain a 

64 storage class override. 

65 explicit_storage_class : `StorageClass` | `str` | `None` 

66 A storage class that the user explicitly requested as an override. 

67 """ 

68 if explicit_storage_class is not None: 

69 return ref.overrideStorageClass(explicit_storage_class) 

70 

71 # If the caller provided a DatasetRef or DatasetType, they may have 

72 # overridden the storage class on it, and we need to propagate that to the 

73 # output. 

74 dataset_type = _extract_dataset_type(original_dataset_ref_or_type) 

75 if dataset_type is not None: 

76 return ref.overrideStorageClass(dataset_type.storageClass) 

77 

78 return ref 

79 

80 

81def make_read_ref( 

82 registry_ref: DatasetRef, 

83 original_dataset_ref_or_type: DatasetRef | DatasetType | str, 

84 explicit_storage_class: StorageClass | str | None, 

85) -> DatasetRef: 

86 """Return the ref describing the dataset the caller asked for. 

87 

88 Parameters 

89 ---------- 

90 registry_ref : `DatasetRef` 

91 The parent (composite) ref returned by the server, carrying the 

92 `StorageClass` from the dataset type definition in the repository. 

93 Component dataset types are never sent to the server, so any component 

94 has to be re-applied here. 

95 original_dataset_ref_or_type : `DatasetRef` | `DatasetType` | `str` 

96 The ref or type that was input to the search, which may name a 

97 component and may override the storage class. 

98 explicit_storage_class : `StorageClass` | `str` | `None` 

99 A storage class that the user explicitly requested as an override. 

100 

101 Returns 

102 ------- 

103 read_ref : `DatasetRef` 

104 The ref describing the component and `StorageClass` that the caller 

105 wants returned. 

106 

107 Notes 

108 ----- 

109 A read `StorageClass` override can define components that the repository 

110 definition does not, so the component is derived from the overridden 

111 composite rather than from ``registry_ref`` itself. 

112 """ 

113 component = get_component_override(original_dataset_ref_or_type) 

114 if component is None: 

115 return apply_storage_class_override( 

116 registry_ref, original_dataset_ref_or_type, explicit_storage_class 

117 ) 

118 

119 # Apply any composite storage class override before deriving the component 

120 # from it. 

121 read_ref = registry_ref 

122 dataset_type = _extract_dataset_type(original_dataset_ref_or_type) 

123 if dataset_type is not None: 

124 if dataset_type.parentStorageClass is not None: 

125 read_ref = read_ref.overrideStorageClass(dataset_type.parentStorageClass) 

126 read_ref = read_ref.makeComponentRef(component).overrideStorageClass(dataset_type.storageClass) 

127 else: 

128 # Only a dataset type name was given, so there is no override. 

129 read_ref = read_ref.makeComponentRef(component) 

130 if explicit_storage_class is not None: 

131 read_ref = read_ref.overrideStorageClass(explicit_storage_class) 

132 

133 return read_ref 

134 

135 

136def normalize_dataset_type_name(datasetTypeOrName: DatasetType | str) -> DatasetTypeName: 

137 """Convert DatasetType parameters in the format used by Butler methods 

138 to a standardized string name for the REST API. 

139 

140 Parameters 

141 ---------- 

142 datasetTypeOrName : `DatasetType` | `str` 

143 A DatasetType, or the name of a DatasetType. This union is a common 

144 parameter in many `Butler` methods. 

145 """ 

146 return DatasetTypeName(get_dataset_type_name(datasetTypeOrName)) 

147 

148 

149def split_dataset_type_name( 

150 datasetTypeOrName: DatasetType | str, 

151) -> tuple[DatasetTypeName, str | None]: 

152 """Split a dataset type parameter into a parent dataset type name and an 

153 optional component name. 

154 

155 Component dataset type names must never be sent to the server -- the 

156 server cannot construct a component `DatasetType` unless it has the 

157 parent's storage class definition available, and storage classes are 

158 often defined by science pipelines packages that are only installed on 

159 the client. Callers should request the parent dataset type from the 

160 server and re-apply the component on the client side. 

161 

162 Parameters 

163 ---------- 

164 datasetTypeOrName : `DatasetType` | `str` 

165 A DatasetType, or the name of a DatasetType. 

166 

167 Returns 

168 ------- 

169 parent_name : `DatasetTypeName` 

170 Name of the parent dataset type, suitable for sending to the server. 

171 component : `str` | `None` 

172 Component name, or `None` if this is not a component dataset type. 

173 

174 Raises 

175 ------ 

176 lsst.daf.butler.DatasetTypeExpressionError 

177 Raised if the given name is not a syntactically valid dataset type 

178 name. Sending such a name to the server would produce a confusing 

179 HTTP error instead of a useful message. 

180 """ 

181 name = get_dataset_type_name(datasetTypeOrName) 

182 validate_dataset_type_name(name) 

183 parent_name, component = DatasetType.splitDatasetTypeName(name) 

184 return DatasetTypeName(parent_name), component 

185 

186 

187def get_component_override(datasetRefOrType: DatasetRef | DatasetType | str) -> str | None: 

188 """Return the component name from a ref or dataset type provided by the 

189 user, or `None` if it does not refer to a component. 

190 

191 Parameters 

192 ---------- 

193 datasetRefOrType : `DatasetRef` | `DatasetType` | `str` 

194 A DatasetRef, DatasetType, or name of a DatasetType. This union is a 

195 common parameter in many `Butler` methods. 

196 """ 

197 if isinstance(datasetRefOrType, DatasetRef): 

198 return datasetRefOrType.datasetType.component() 

199 _, component = split_dataset_type_name(datasetRefOrType) 

200 return component 

201 

202 

203def simplify_dataId(dataId: DataId | None, kwargs: dict[str, DataIdValue]) -> SerializedDataId: 

204 """Take a generic Data ID and convert it to a serializable form. 

205 

206 Parameters 

207 ---------- 

208 dataId : `dict`, `None`, `DataCoordinate` 

209 The data ID to serialize. 

210 kwargs : `dict` 

211 Additional entries to augment or replace the values in ``dataId``. 

212 

213 Returns 

214 ------- 

215 data_id : `SerializedDataId` 

216 A serializable form. 

217 """ 

218 if dataId is None: 

219 dataId = {} 

220 elif isinstance(dataId, DataCoordinate): 

221 dataId = dataId.to_simple(minimal=True).dataId 

222 else: 

223 dataId = dict(dataId) 

224 

225 return _SERIALIZED_DATA_ID_TYPE_ADAPTER.validate_python(dataId | kwargs) 

226 

227 

228def _extract_dataset_type(datasetRefOrType: DatasetRef | DatasetType | str) -> DatasetType | None: 

229 """Return the DatasetType associated with the argument, or None if the 

230 argument is not an object that contains a DatasetType object. 

231 

232 Parameters 

233 ---------- 

234 datasetRefOrType : `DatasetRef` | `DatasetType` | `str` 

235 A DatasetRef, DatasetType, or name of a DatasetType. This union is a 

236 common parameter in many `Butler` methods. 

237 """ 

238 if isinstance(datasetRefOrType, DatasetType): 

239 return datasetRefOrType 

240 elif isinstance(datasetRefOrType, DatasetRef): 

241 return datasetRefOrType.datasetType 

242 else: 

243 return None