Coverage for src/hdmf/validate/errors.py: 90%

112 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-07-10 23:48 +0000

1from numpy import dtype 

2 

3from ..spec.spec import DtypeHelper 

4from ..utils import docval, getargs 

5 

6__all__ = [ 

7 "Error", 

8 "DtypeError", 

9 "MissingError", 

10 "ExpectedArrayError", 

11 "ShapeError", 

12 "MissingDataType", 

13 "IllegalLinkError", 

14 "IncorrectDataType", 

15 "IncorrectQuantityError" 

16] 

17 

18 

19class Error: 

20 

21 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

22 {'name': 'reason', 'type': str, 'doc': 'the reason for the error'}, 

23 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}) 

24 def __init__(self, **kwargs): 

25 self.__name = getargs('name', kwargs) 

26 self.__reason = getargs('reason', kwargs) 

27 self.__location = getargs('location', kwargs) 

28 

29 @property 

30 def name(self): 

31 return self.__name 

32 

33 @property 

34 def reason(self): 

35 return self.__reason 

36 

37 @property 

38 def location(self): 

39 return self.__location 

40 

41 @location.setter 

42 def location(self, loc): 

43 self.__location = loc 

44 

45 def __str__(self): 

46 return self.__format_str(self.name, self.location, self.reason) 

47 

48 @staticmethod 

49 def __format_str(name, location, reason): 

50 if location is not None: 

51 return "%s (%s): %s" % (name, location, reason) 

52 else: 

53 return "%s: %s" % (name, reason) 

54 

55 def __repr__(self): 

56 return self.__str__() 

57 

58 def __hash__(self): 

59 """Returns the hash value of this Error 

60 

61 Note: if the location property is set after creation, the hash value will 

62 change. Therefore, it is important to finalize the value of location 

63 before getting the hash value. 

64 """ 

65 return hash(self.__equatable_str()) 

66 

67 def __equatable_str(self): 

68 """A string representation of the error which can be used to check for equality 

69 

70 For a single error, name can end up being different depending on whether it is 

71 generated from a base data type spec or from an inner type definition. These errors 

72 should still be considered equal because they are caused by the same problem. 

73 

74 When a location is provided, we only consider the name of the field and drop the 

75 rest of the spec name. However, when a location is not available, then we need to 

76 use the fully-provided name. 

77 """ 

78 if self.location is not None: 

79 equatable_name = self.name.split('/')[-1] 

80 else: 

81 equatable_name = self.name 

82 return self.__format_str(equatable_name, self.location, self.reason) 

83 

84 def __eq__(self, other): 

85 return hash(self) == hash(other) 

86 

87 

88class DtypeError(Error): 

89 

90 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

91 {'name': 'expected', 'type': (dtype, type, str, list), 'doc': 'the expected dtype'}, 

92 {'name': 'received', 'type': (dtype, type, str, list), 'doc': 'the received dtype'}, 

93 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}) 

94 def __init__(self, **kwargs): 

95 name = getargs('name', kwargs) 

96 expected = getargs('expected', kwargs) 

97 received = getargs('received', kwargs) 

98 if isinstance(expected, list): 98 ↛ 99line 98 didn't jump to line 99, because the condition on line 98 was never true

99 expected = DtypeHelper.simplify_cpd_type(expected) 

100 reason = "incorrect type - expected '%s', got '%s'" % (expected, received) 

101 loc = getargs('location', kwargs) 

102 super().__init__(name, reason, location=loc) 

103 

104 

105class MissingError(Error): 

106 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

107 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}) 

108 def __init__(self, **kwargs): 

109 name = getargs('name', kwargs) 

110 reason = "argument missing" 

111 loc = getargs('location', kwargs) 

112 super().__init__(name, reason, location=loc) 

113 

114 

115class MissingDataType(Error): 

116 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

117 {'name': 'data_type', 'type': str, 'doc': 'the missing data type'}, 

118 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}, 

119 {'name': 'missing_dt_name', 'type': str, 'doc': 'the name of the missing data type', 'default': None}) 

120 def __init__(self, **kwargs): 

121 name, data_type, missing_dt_name = getargs('name', 'data_type', 'missing_dt_name', kwargs) 

122 self.__data_type = data_type 

123 if missing_dt_name is not None: 

124 reason = "missing data type %s (%s)" % (self.__data_type, missing_dt_name) 

125 else: 

126 reason = "missing data type %s" % self.__data_type 

127 loc = getargs('location', kwargs) 

128 super().__init__(name, reason, location=loc) 

129 

130 @property 

131 def data_type(self): 

132 return self.__data_type 

133 

134 

135class IncorrectQuantityError(Error): 

136 """A validation error indicating that a child group/dataset/link has the incorrect quantity of matching elements""" 

137 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

138 {'name': 'data_type', 'type': str, 'doc': 'the data type which has the incorrect quantity'}, 

139 {'name': 'expected', 'type': (str, int), 'doc': 'the expected quantity'}, 

140 {'name': 'received', 'type': (str, int), 'doc': 'the received quantity'}, 

141 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}) 

142 def __init__(self, **kwargs): 

143 name, data_type, expected, received = getargs('name', 'data_type', 'expected', 'received', kwargs) 

144 reason = "expected a quantity of %s for data type %s, received %s" % (str(expected), data_type, str(received)) 

145 loc = getargs('location', kwargs) 

146 super().__init__(name, reason, location=loc) 

147 

148 

149class ExpectedArrayError(Error): 

150 

151 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

152 {'name': 'expected', 'type': (tuple, list), 'doc': 'the expected shape'}, 

153 {'name': 'received', 'type': str, 'doc': 'the received data'}, 

154 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}) 

155 def __init__(self, **kwargs): 

156 name = getargs('name', kwargs) 

157 expected = getargs('expected', kwargs) 

158 received = getargs('received', kwargs) 

159 reason = "incorrect shape - expected an array of shape '%s', got non-array data '%s'" % (expected, received) 

160 loc = getargs('location', kwargs) 

161 super().__init__(name, reason, location=loc) 

162 

163 

164class ShapeError(Error): 

165 

166 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

167 {'name': 'expected', 'type': (tuple, list), 'doc': 'the expected shape'}, 

168 {'name': 'received', 'type': (tuple, list), 'doc': 'the received shape'}, 

169 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}) 

170 def __init__(self, **kwargs): 

171 name = getargs('name', kwargs) 

172 expected = getargs('expected', kwargs) 

173 received = getargs('received', kwargs) 

174 reason = "incorrect shape - expected '%s', got '%s'" % (expected, received) 

175 loc = getargs('location', kwargs) 

176 super().__init__(name, reason, location=loc) 

177 

178 

179class IllegalLinkError(Error): 

180 """ 

181 A validation error for indicating that a link was used where an actual object 

182 (i.e. a dataset or a group) must be used 

183 """ 

184 

185 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

186 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}) 

187 def __init__(self, **kwargs): 

188 name = getargs('name', kwargs) 

189 reason = "illegal use of link (linked object will not be validated)" 

190 loc = getargs('location', kwargs) 

191 super().__init__(name, reason, location=loc) 

192 

193 

194class IncorrectDataType(Error): 

195 """ 

196 A validation error for indicating that the incorrect data_type (not dtype) was used. 

197 """ 

198 

199 @docval({'name': 'name', 'type': str, 'doc': 'the name of the component that is erroneous'}, 

200 {'name': 'expected', 'type': str, 'doc': 'the expected data_type'}, 

201 {'name': 'received', 'type': str, 'doc': 'the received data_type'}, 

202 {'name': 'location', 'type': str, 'doc': 'the location of the error', 'default': None}) 

203 def __init__(self, **kwargs): 

204 name = getargs('name', kwargs) 

205 expected = getargs('expected', kwargs) 

206 received = getargs('received', kwargs) 

207 reason = "incorrect data_type - expected '%s', got '%s'" % (expected, received) 

208 loc = getargs('location', kwargs) 

209 super().__init__(name, reason, location=loc)