Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# 

2# LSST Data Management System 

3# Copyright 2008, 2009, 2010, 2011 LSST Corporation. 

4# 

5# This product includes software developed by the 

6# LSST Project (http://www.lsst.org/). 

7# 

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

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

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

11# (at your option) any later version. 

12# 

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

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

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

16# GNU General Public License for more details. 

17# 

18# You should have received a copy of the LSST License Statement and 

19# the GNU General Public License along with this program. If not, 

20# see <http://www.lsstcorp.org/LegalNotices/>. 

21# 

22 

23__all__ = ["Struct"] 

24 

25 

26class Struct: 

27 """A container to which you can add fields as attributes. 

28 

29 Parameters 

30 ---------- 

31 keyArgs 

32 keyword arguments specifying fields and their values. 

33 

34 Notes 

35 ----- 

36 Intended to be used for the return value from `~lsst.pipe.base.Task.run` 

37 and other `~lsst.pipe.base.Task` methods, and useful for any method that 

38 returns multiple values. 

39 

40 The intent is to allow accessing returned items by name, instead of 

41 unpacking a tuple. This makes the code much more robust and easier to 

42 read. It allows one to change what values are returned without inducing 

43 mysterious failures: adding items is completely safe, and removing or 

44 renaming items causes errors that are caught quickly and reported in a way 

45 that is easy to understand. 

46 

47 The primary reason for using Struct instead of dict is that the fields may 

48 be accessed as attributes, e.g. ``aStruct.foo`` instead of 

49 ``aDict["foo"]``. Admittedly this only saves a few characters, but it 

50 makes the code significantly more readable. 

51 

52 Struct is preferred over named tuples, because named tuples can be used as 

53 ordinary tuples, thus losing all the safety advantages of Struct. In 

54 addition, named tuples are clumsy to define and Structs are much more 

55 mutable (e.g. one can trivially combine Structs and add additional fields). 

56 

57 Examples 

58 -------- 

59 >>> myStruct = Struct( 

60 >>> strVal = 'the value of the field named "strVal"', 

61 >>> intVal = 35, 

62 >>> ) 

63 

64 """ 

65 

66 def __init__(self, **keyArgs): 

67 for name, val in keyArgs.items(): 

68 self.__safeAdd(name, val) 

69 

70 def __safeAdd(self, name, val): 

71 """Add a field if it does not already exist and name does not start 

72 with ``__`` (two underscores). 

73 

74 Parameters 

75 ---------- 

76 name : `str` 

77 Name of field to add. 

78 val : object 

79 Value of field to add. 

80 

81 Raises 

82 ------ 

83 RuntimeError 

84 Raised if name already exists or starts with ``__`` (two 

85 underscores). 

86 """ 

87 if hasattr(self, name): 

88 raise RuntimeError(f"Item {name!r} already exists") 

89 if name.startswith("__"): 

90 raise RuntimeError(f"Item name {name!r} invalid; must not begin with __") 

91 setattr(self, name, val) 

92 

93 def getDict(self): 

94 """Get a dictionary of fields in this struct. 

95 

96 Returns 

97 ------- 

98 structDict : `dict` 

99 Dictionary with field names as keys and field values as values. 

100 The values are shallow copies. 

101 """ 

102 return self.__dict__.copy() 

103 

104 def mergeItems(self, struct, *nameList): 

105 """Copy specified fields from another struct, provided they don't 

106 already exist. 

107 

108 Parameters 

109 ---------- 

110 struct : `Struct` 

111 `Struct` from which to copy. 

112 *nameList : `str` 

113 All remaining arguments are names of items to copy. 

114 

115 Raises 

116 ------ 

117 RuntimeError 

118 Raised if any item in nameList already exists in self (but any 

119 items before the conflicting item in nameList will have been 

120 copied). 

121 

122 Examples 

123 -------- 

124 For example:: 

125 

126 foo.copyItems(other, "itemName1", "itemName2") 

127 

128 copies ``other.itemName1`` and ``other.itemName2`` into self. 

129 """ 

130 for name in nameList: 

131 self.__safeAdd(name, getattr(struct, name)) 

132 

133 def copy(self): 

134 """Make a one-level-deep copy (values are not copied). 

135 

136 Returns 

137 ------- 

138 copy : `Struct` 

139 One-level-deep copy of this Struct. 

140 """ 

141 return Struct(**self.getDict()) 

142 

143 def __eq__(self, other): 

144 return self.__dict__ == other.__dict__ 

145 

146 def __len__(self): 

147 return len(self.__dict__) 

148 

149 def __repr__(self): 

150 itemsStr = "; ".join(f"{name}={val}" for name, val in self.getDict().items()) 

151 return f"{self.__class__.__name__}({itemsStr})"