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

61 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-03-22 02:08 -0700

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 ) -> Any: 

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

48 

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

50 parameters. 

51 

52 Parameters 

53 ---------- 

54 component : `str` or None 

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

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

57 parameters : `dict` or None 

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

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

60 be merged with the parameters dict used to construct the 

61 `DeferredDatasetHandle` class. 

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

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

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

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

66 different type to be returned. 

67 This type must be compatible with the original type. 

68 

69 Returns 

70 ------- 

71 return : `object` 

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

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

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

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

76 parameters. 

77 

78 Raises 

79 ------ 

80 KeyError 

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

82 class can be found. 

83 """ 

84 if self.inMemoryDataset is None: 

85 return None 

86 

87 if self.parameters is not None: 

88 mergedParameters = self.parameters.copy() 

89 if parameters is not None: 

90 mergedParameters.update(parameters) 

91 elif parameters is not None: 

92 mergedParameters = parameters 

93 else: 

94 mergedParameters = {} 

95 

96 returnStorageClass: StorageClass | None = None 

97 if storageClass: 

98 if isinstance(storageClass, str): 

99 factory = StorageClassFactory() 

100 returnStorageClass = factory.getStorageClass(storageClass) 

101 else: 

102 returnStorageClass = storageClass 

103 

104 if component or mergedParameters: 

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

106 # class. 

107 thisStorageClass = self._getStorageClass() 

108 inMemoryDataset = self.inMemoryDataset 

109 

110 # Parameters for derived components are applied against the 

111 # composite. 

112 if component in thisStorageClass.derivedComponents: 

113 thisStorageClass.validateParameters(parameters) 

114 

115 # Process the parameters (hoping this never modified the 

116 # original object). 

117 inMemoryDataset = thisStorageClass.delegate().handleParameters( 

118 inMemoryDataset, mergedParameters 

119 ) 

120 mergedParameters = {} # They have now been used 

121 

122 readStorageClass = thisStorageClass.derivedComponents[component] 

123 else: 

124 if component: 

125 readStorageClass = thisStorageClass.components[component] 

126 else: 

127 readStorageClass = thisStorageClass 

128 readStorageClass.validateParameters(mergedParameters) 

129 

130 if component: 

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

132 

133 if mergedParameters: 

134 inMemoryDataset = readStorageClass.delegate().handleParameters( 

135 inMemoryDataset, mergedParameters 

136 ) 

137 if returnStorageClass: 

138 return returnStorageClass.coerce_type(inMemoryDataset) 

139 return inMemoryDataset 

140 else: 

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

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

143 if returnStorageClass: 

144 return returnStorageClass.coerce_type(self.inMemoryDataset) 

145 return self.inMemoryDataset 

146 

147 def _getStorageClass(self) -> StorageClass: 

148 """Return the relevant storage class. 

149 

150 Returns 

151 ------- 

152 storageClass : `StorageClass` 

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

154 from the python type of the stored object. 

155 

156 Raises 

157 ------ 

158 KeyError 

159 Raised if the storage class could not be found. 

160 """ 

161 factory = StorageClassFactory() 

162 if self.storageClass: 

163 return factory.getStorageClass(self.storageClass) 

164 

165 # Need to match python type. 

166 pytype = type(self.inMemoryDataset) 

167 return factory.findStorageClass(pytype) 

168 

169 inMemoryDataset: Any 

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

171 """ 

172 

173 storageClass: Optional[str] = None 

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

175 dataset. 

176 

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

178 """ 

179 

180 parameters: Optional[dict] = None 

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

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

183 """ 

184 

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

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

187 handle. 

188 """