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

55 statements  

« prev     ^ index     » next       coverage.py v6.4.2, created at 2022-08-03 02:43 -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 

29from lsst.utils.introspection import get_full_type_name 

30 

31 

32# Use an empty dataID as a default. 

33def _default_dataId() -> DataCoordinate: 

34 return DataCoordinate.makeEmpty(DimensionUniverse()) 

35 

36 

37@dataclasses.dataclass(frozen=True) 

38class InMemoryDatasetHandle: 

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

40 

41 def get( 

42 self, *, component: Optional[str] = None, parameters: Optional[dict] = None, **kwargs: dict 

43 ) -> Any: 

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

45 

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

47 parameters. 

48 

49 Parameters 

50 ---------- 

51 component : `str` or None 

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

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

54 parameters : `dict` or None 

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

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

57 be merged with the parameters dict used to construct the 

58 `DeferredDatasetHandle` class. 

59 **kwargs 

60 This argument is deprecated and only exists to support legacy 

61 gen2 butler code during migration. It is completely ignored 

62 and will be removed in the future. 

63 

64 Returns 

65 ------- 

66 return : `object` 

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

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

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

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

71 parameters. 

72 """ 

73 if self.inMemoryDataset is None: 

74 return None 

75 

76 if self.parameters is not None: 

77 mergedParameters = self.parameters.copy() 

78 if parameters is not None: 

79 mergedParameters.update(parameters) 

80 elif parameters is not None: 

81 mergedParameters = parameters 

82 else: 

83 mergedParameters = {} 

84 

85 if component or mergedParameters: 

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

87 # class. 

88 storageClass = self._getStorageClass() 

89 inMemoryDataset = self.inMemoryDataset 

90 

91 # Parameters for derived components are applied against the 

92 # composite. 

93 if component in storageClass.derivedComponents: 

94 storageClass.validateParameters(parameters) 

95 

96 # Process the parameters (hoping this never modified the 

97 # original object). 

98 inMemoryDataset = storageClass.delegate().handleParameters(inMemoryDataset, mergedParameters) 

99 mergedParameters = {} # They have now been used 

100 

101 readStorageClass = storageClass.derivedComponents[component] 

102 else: 

103 if component: 

104 readStorageClass = storageClass.components[component] 

105 else: 

106 readStorageClass = storageClass 

107 readStorageClass.validateParameters(mergedParameters) 

108 

109 if component: 

110 inMemoryDataset = storageClass.delegate().getComponent(inMemoryDataset, component) 

111 

112 if mergedParameters: 

113 inMemoryDataset = readStorageClass.delegate().handleParameters( 

114 inMemoryDataset, mergedParameters 

115 ) 

116 

117 return inMemoryDataset 

118 else: 

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

120 # can be returned as is. 

121 return self.inMemoryDataset 

122 

123 def _getStorageClass(self) -> StorageClass: 

124 factory = StorageClassFactory() 

125 if self.storageClass: 

126 return factory.getStorageClass(self.storageClass) 

127 

128 # Need to match python type. 

129 pytype = type(self.inMemoryDataset) 

130 for storageClass in factory.values(): 

131 # It is possible for a single python type to refer to multiple 

132 # storage classes such that this could be quite fragile. 

133 if storageClass.is_type(pytype): 

134 return storageClass 

135 

136 raise ValueError( 

137 "Unable to find a StorageClass with associated with type " 

138 f"{get_full_type_name(self.inMemoryDataset)}" 

139 ) 

140 

141 inMemoryDataset: Any 

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

143 """ 

144 

145 storageClass: Optional[str] = None 

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

147 dataset. 

148 

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

150 """ 

151 

152 parameters: Optional[dict] = None 

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

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

155 """ 

156 

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

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

159 handle. 

160 """