Coverage for python/lsst/pipe/base/_dataset_handle.py: 22%

61 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-02-10 02:03 -0800

1# This file is part of pipe_base. 

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 program is free software: you can redistribute it and/or modify 

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

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

12# (at your option) any later version. 

13# 

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

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

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

17# GNU General Public License for more details. 

18# 

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

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

21from __future__ import annotations 

22 

23__all__ = ["InMemoryDatasetHandle"] 

24 

25import dataclasses 

26from typing import Any, Optional 

27 

28from lsst.daf.butler import DataCoordinate, DimensionUniverse, StorageClass, StorageClassFactory 

29 

30 

31# Use an empty dataID as a default. 

32def _default_dataId() -> DataCoordinate: 

33 return DataCoordinate.makeEmpty(DimensionUniverse()) 

34 

35 

36@dataclasses.dataclass(frozen=True) 

37class InMemoryDatasetHandle: 

38 """An in-memory version of a `~lsst.daf.butler.DeferredDatasetHandle`.""" 

39 

40 def get( 

41 self, 

42 *, 

43 component: Optional[str] = None, 

44 parameters: Optional[dict] = None, 

45 storageClass: str | StorageClass | None = None, 

46 **kwargs: dict, 

47 ) -> Any: 

48 """Retrieves the dataset pointed to by this handle 

49 

50 This handle may be used multiple times, possibly with different 

51 parameters. 

52 

53 Parameters 

54 ---------- 

55 component : `str` or None 

56 If the deferred object is a component dataset type, this parameter 

57 may specify the name of the component to use in the get operation. 

58 parameters : `dict` or None 

59 The parameters argument will be passed to the butler get method. 

60 It defaults to None. If the value is not None, this dict will 

61 be merged with the parameters dict used to construct the 

62 `DeferredDatasetHandle` class. 

63 storageClass : `StorageClass` or `str`, optional 

64 The storage class to be used to override the Python type 

65 returned by this method. By default the returned type matches 

66 the type stored. Specifying a read `StorageClass` can force a 

67 different type to be returned. 

68 This type must be compatible with the original type. 

69 **kwargs 

70 This argument is deprecated and only exists to support legacy 

71 gen2 butler code during migration. It is completely ignored 

72 and will be removed in the future. 

73 

74 Returns 

75 ------- 

76 return : `object` 

77 The dataset pointed to by this handle. This is the actual object 

78 that was initially stored and not a copy. Modifying this object 

79 will modify the stored object. If the stored object is `None` this 

80 method always returns `None` regardless of any component request or 

81 parameters. 

82 

83 Raises 

84 ------ 

85 KeyError 

86 Raised if a component or parameters are used but no storage 

87 class can be found. 

88 """ 

89 if self.inMemoryDataset is None: 

90 return None 

91 

92 if self.parameters is not None: 

93 mergedParameters = self.parameters.copy() 

94 if parameters is not None: 

95 mergedParameters.update(parameters) 

96 elif parameters is not None: 

97 mergedParameters = parameters 

98 else: 

99 mergedParameters = {} 

100 

101 returnStorageClass: StorageClass | None = None 

102 if storageClass: 

103 if isinstance(storageClass, str): 

104 factory = StorageClassFactory() 

105 returnStorageClass = factory.getStorageClass(storageClass) 

106 else: 

107 returnStorageClass = storageClass 

108 

109 if component or mergedParameters: 

110 # This requires a storage class look up to locate the delegate 

111 # class. 

112 thisStorageClass = self._getStorageClass() 

113 inMemoryDataset = self.inMemoryDataset 

114 

115 # Parameters for derived components are applied against the 

116 # composite. 

117 if component in thisStorageClass.derivedComponents: 

118 thisStorageClass.validateParameters(parameters) 

119 

120 # Process the parameters (hoping this never modified the 

121 # original object). 

122 inMemoryDataset = thisStorageClass.delegate().handleParameters( 

123 inMemoryDataset, mergedParameters 

124 ) 

125 mergedParameters = {} # They have now been used 

126 

127 readStorageClass = thisStorageClass.derivedComponents[component] 

128 else: 

129 if component: 

130 readStorageClass = thisStorageClass.components[component] 

131 else: 

132 readStorageClass = thisStorageClass 

133 readStorageClass.validateParameters(mergedParameters) 

134 

135 if component: 

136 inMemoryDataset = thisStorageClass.delegate().getComponent(inMemoryDataset, component) 

137 

138 if mergedParameters: 

139 inMemoryDataset = readStorageClass.delegate().handleParameters( 

140 inMemoryDataset, mergedParameters 

141 ) 

142 if returnStorageClass: 

143 return returnStorageClass.coerce_type(inMemoryDataset) 

144 return inMemoryDataset 

145 else: 

146 # If there are no parameters or component requests the object 

147 # can be returned as is, but possibly with conversion. 

148 if returnStorageClass: 

149 return returnStorageClass.coerce_type(self.inMemoryDataset) 

150 return self.inMemoryDataset 

151 

152 def _getStorageClass(self) -> StorageClass: 

153 """Return the relevant storage class. 

154 

155 Returns 

156 ------- 

157 storageClass : `StorageClass` 

158 The storage class associated with this handle, or one derived 

159 from the python type of the stored object. 

160 

161 Raises 

162 ------ 

163 KeyError 

164 Raised if the storage class could not be found. 

165 """ 

166 factory = StorageClassFactory() 

167 if self.storageClass: 

168 return factory.getStorageClass(self.storageClass) 

169 

170 # Need to match python type. 

171 pytype = type(self.inMemoryDataset) 

172 return factory.findStorageClass(pytype) 

173 

174 inMemoryDataset: Any 

175 """The object to store in this dataset handle for later retrieval. 

176 """ 

177 

178 storageClass: Optional[str] = None 

179 """The name of the `~lsst.daf.butler.StorageClass` associated with this 

180 dataset. 

181 

182 If `None`, the storage class will be looked up from the factory. 

183 """ 

184 

185 parameters: Optional[dict] = None 

186 """Optional parameters that may be used to specify a subset of the dataset 

187 to be loaded (`dict` or `None`). 

188 """ 

189 

190 dataId: DataCoordinate = dataclasses.field(default_factory=_default_dataId) 

191 """The `~lsst.daf.butler.DataCoordinate` associated with this dataset 

192 handle. 

193 """