Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tables/misc/enum.py: 25%

76 statements  

« prev     ^ index     » next       coverage.py v7.2.5, created at 2023-05-10 06:15 +0000

1"""Implementation of enumerated types. 

2 

3This module provides the `Enum` class, which can be used to construct 

4enumerated types. Those types are defined by providing an *exhaustive 

5set or list* of possible, named values for a variable of that type. 

6Enumerated variables of the same type are usually compared between them 

7for equality and sometimes for order, but are not usually operated upon. 

8 

9Enumerated values have an associated *name* and *concrete value*. Every 

10name is unique and so are concrete values. An enumerated variable 

11always takes the concrete value, not its name. Usually, the concrete 

12value is not used directly, and frequently it is entirely irrelevant. 

13For the same reason, an enumerated variable is not usually compared with 

14concrete values out of its enumerated type. For that kind of use, 

15standard variables and constants are more adequate. 

16 

17""" 

18 

19 

20__docformat__ = 'reStructuredText' 

21"""The format of documentation strings in this module.""" 

22 

23 

24class Enum: 

25 """Enumerated type. 

26 

27 Each instance of this class represents an enumerated type. The 

28 values of the type must be declared 

29 *exhaustively* and named with 

30 *strings*, and they might be given explicit 

31 concrete values, though this is not compulsory. Once the type is 

32 defined, it can not be modified. 

33 

34 There are three ways of defining an enumerated type. Each one 

35 of them corresponds to the type of the only argument in the 

36 constructor of Enum: 

37 

38 - *Sequence of names*: each enumerated 

39 value is named using a string, and its order is determined by 

40 its position in the sequence; the concrete value is assigned 

41 automatically:: 

42 

43 >>> boolEnum = Enum(['True', 'False']) 

44 

45 - *Mapping of names*: each enumerated 

46 value is named by a string and given an explicit concrete value. 

47 All of the concrete values must be different, or a 

48 ValueError will be raised:: 

49 

50 >>> priority = Enum({'red': 20, 'orange': 10, 'green': 0}) 

51 >>> colors = Enum({'red': 1, 'blue': 1}) 

52 Traceback (most recent call last): 

53 ... 

54 ValueError: enumerated values contain duplicate concrete values: 1 

55 

56 - *Enumerated type*: in that case, a copy 

57 of the original enumerated type is created. Both enumerated 

58 types are considered equal:: 

59 

60 >>> prio2 = Enum(priority) 

61 >>> priority == prio2 

62 True 

63 

64 Please note that names starting with _ are 

65 not allowed, since they are reserved for internal usage:: 

66 

67 >>> prio2 = Enum(['_xx']) 

68 Traceback (most recent call last): 

69 ... 

70 ValueError: name of enumerated value can not start with ``_``: '_xx' 

71 

72 The concrete value of an enumerated value is obtained by 

73 getting its name as an attribute of the Enum 

74 instance (see __getattr__()) or as an item (see 

75 __getitem__()). This allows comparisons between 

76 enumerated values and assigning them to ordinary Python 

77 variables:: 

78 

79 >>> redv = priority.red 

80 >>> redv == priority['red'] 

81 True 

82 >>> redv > priority.green 

83 True 

84 >>> priority.red == priority.orange 

85 False 

86 

87 The name of the enumerated value corresponding to a concrete 

88 value can also be obtained by using the 

89 __call__() method of the enumerated type. In this 

90 way you get the symbolic name to use it later with 

91 __getitem__():: 

92 

93 >>> priority(redv) 

94 'red' 

95 >>> priority.red == priority[priority(priority.red)] 

96 True 

97 

98 (If you ask, the __getitem__() method is 

99 not used for this purpose to avoid ambiguity in the case of using 

100 strings as concrete values.) 

101 

102 """ 

103 

104 def __init__(self, enum): 

105 mydict = self.__dict__ 

106 

107 mydict['_names'] = {} 

108 mydict['_values'] = {} 

109 

110 if isinstance(enum, list) or isinstance(enum, tuple): 

111 for (value, name) in enumerate(enum): # values become 0, 1, 2... 

112 self._check_and_set_pair(name, value) 

113 elif isinstance(enum, dict): 

114 for (name, value) in enum.items(): 

115 self._check_and_set_pair(name, value) 

116 elif isinstance(enum, Enum): 

117 for (name, value) in enum._names.items(): 

118 self._check_and_set_pair(name, value) 

119 else: 

120 raise TypeError("""\ 

121enumerations can only be created from \ 

122sequences, mappings and other enumerations""") 

123 

124 def _check_and_set_pair(self, name, value): 

125 """Check validity of enumerated value and insert it into type.""" 

126 

127 names = self._names 

128 values = self._values 

129 

130 if not isinstance(name, str): 

131 raise TypeError( 

132 f"name of enumerated value is not a string: {name!r}") 

133 if name.startswith('_'): 

134 raise ValueError( 

135 "name of enumerated value can not start with ``_``: %r" 

136 % name) 

137 # This check is only necessary with a sequence base object. 

138 if name in names: 

139 raise ValueError( 

140 "enumerated values contain duplicate names: %r" % name) 

141 # This check is only necessary with a mapping base object. 

142 if value in values: 

143 raise ValueError( 

144 "enumerated values contain duplicate concrete values: %r" 

145 % value) 

146 

147 names[name] = value 

148 values[value] = name 

149 self.__dict__[name] = value 

150 

151 def __getitem__(self, name): 

152 """Get the concrete value of the enumerated value with that name. 

153 

154 The name of the enumerated value must be a string. If there is no value 

155 with that name in the enumeration, a KeyError is raised. 

156 

157 Examples 

158 -------- 

159 

160 Let ``enum`` be an enumerated type defined as: 

161 

162 >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5}) 

163 

164 then: 

165 

166 >>> enum['T1'] 

167 2 

168 >>> enum['foo'] 

169 Traceback (most recent call last): 

170 ... 

171 KeyError: "no enumerated value with that name: 'foo'" 

172 

173 """ 

174 

175 try: 

176 return self._names[name] 

177 except KeyError: 

178 raise KeyError(f"no enumerated value with that name: {name!r}") 

179 

180 def __setitem__(self, name, value): 

181 """This operation is forbidden.""" 

182 raise IndexError("operation not allowed") 

183 

184 def __delitem__(self, name): 

185 """This operation is forbidden.""" 

186 raise IndexError("operation not allowed") 

187 

188 def __getattr__(self, name): 

189 """Get the concrete value of the enumerated value with that name. 

190 

191 The name of the enumerated value must be a string. If there is no value 

192 with that name in the enumeration, an AttributeError is raised. 

193 

194 Examples 

195 -------- 

196 Let ``enum`` be an enumerated type defined as: 

197 

198 >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5}) 

199 

200 then: 

201 

202 >>> enum.T1 

203 2 

204 >>> enum.foo 

205 Traceback (most recent call last): 

206 ... 

207 AttributeError: no enumerated value with that name: 'foo' 

208 

209 """ 

210 

211 try: 

212 return self[name] 

213 except KeyError as ke: 

214 raise AttributeError(*ke.args) 

215 

216 def __setattr__(self, name, value): 

217 """This operation is forbidden.""" 

218 raise AttributeError("operation not allowed") 

219 

220 def __delattr__(self, name): 

221 """This operation is forbidden.""" 

222 raise AttributeError("operation not allowed") 

223 

224 def __contains__(self, name): 

225 """Is there an enumerated value with that name in the type? 

226 

227 If the enumerated type has an enumerated value with that name, True is 

228 returned. Otherwise, False is returned. The name must be a string. 

229 

230 This method does *not* check for concrete values matching a value in an 

231 enumerated type. For that, please use the :meth:`Enum.__call__` method. 

232 

233 Examples 

234 -------- 

235 Let ``enum`` be an enumerated type defined as: 

236 

237 >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5}) 

238 

239 then: 

240 

241 >>> 'T1' in enum 

242 True 

243 >>> 'foo' in enum 

244 False 

245 >>> 0 in enum 

246 Traceback (most recent call last): 

247 ... 

248 TypeError: name of enumerated value is not a string: 0 

249 >>> enum.T1 in enum # Be careful with this! 

250 Traceback (most recent call last): 

251 ... 

252 TypeError: name of enumerated value is not a string: 2 

253 

254 """ 

255 

256 if not isinstance(name, str): 

257 raise TypeError( 

258 f"name of enumerated value is not a string: {name!r}") 

259 return name in self._names 

260 

261 def __call__(self, value, *default): 

262 """Get the name of the enumerated value with that concrete value. 

263 

264 If there is no value with that concrete value in the enumeration and a 

265 second argument is given as a default, this is returned. Else, a 

266 ValueError is raised. 

267 

268 This method can be used for checking that a concrete value belongs to 

269 the set of concrete values in an enumerated type. 

270 

271 Examples 

272 -------- 

273 Let ``enum`` be an enumerated type defined as: 

274 

275 >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5}) 

276 

277 then: 

278 

279 >>> enum(5) 

280 'T2' 

281 >>> enum(42, None) is None 

282 True 

283 >>> enum(42) 

284 Traceback (most recent call last): 

285 ... 

286 ValueError: no enumerated value with that concrete value: 42 

287 

288 """ 

289 

290 try: 

291 return self._values[value] 

292 except KeyError: 

293 if len(default) > 0: 

294 return default[0] 

295 raise ValueError( 

296 f"no enumerated value with that concrete value: {value!r}") 

297 

298 def __len__(self): 

299 """Return the number of enumerated values in the enumerated type. 

300 

301 Examples 

302 -------- 

303 >>> len(Enum(['e%d' % i for i in range(10)])) 

304 10 

305 

306 """ 

307 

308 return len(self._names) 

309 

310 def __iter__(self): 

311 """Iterate over the enumerated values. 

312 

313 Enumerated values are returned as (name, value) pairs *in no particular 

314 order*. 

315 

316 Examples 

317 -------- 

318 >>> enumvals = {'red': 4, 'green': 2, 'blue': 1} 

319 >>> enum = Enum(enumvals) 

320 >>> enumdict = dict([(name, value) for (name, value) in enum]) 

321 >>> enumvals == enumdict 

322 True 

323 

324 """ 

325 

326 yield from self._names.items() 

327 

328 def __eq__(self, other): 

329 """Is the other enumerated type equivalent to this one? 

330 

331 Two enumerated types are equivalent if they have exactly the same 

332 enumerated values (i.e. with the same names and concrete values). 

333 

334 Examples 

335 -------- 

336 

337 Let ``enum*`` be enumerated types defined as: 

338 

339 >>> enum1 = Enum({'T0': 0, 'T1': 2}) 

340 >>> enum2 = Enum(enum1) 

341 >>> enum3 = Enum({'T1': 2, 'T0': 0}) 

342 >>> enum4 = Enum({'T0': 0, 'T1': 2, 'T2': 5}) 

343 >>> enum5 = Enum({'T0': 0}) 

344 >>> enum6 = Enum({'T0': 10, 'T1': 20}) 

345 

346 then: 

347 

348 >>> enum1 == enum1 

349 True 

350 >>> enum1 == enum2 == enum3 

351 True 

352 >>> enum1 == enum4 

353 False 

354 >>> enum5 == enum1 

355 False 

356 >>> enum1 == enum6 

357 False 

358 

359 Comparing enumerated types with other kinds of objects produces 

360 a false result: 

361 

362 >>> enum1 == {'T0': 0, 'T1': 2} 

363 False 

364 >>> enum1 == ['T0', 'T1'] 

365 False 

366 >>> enum1 == 2 

367 False 

368 

369 """ 

370 

371 if not isinstance(other, Enum): 

372 return False 

373 return self._names == other._names 

374 

375 def __ne__(self, other): 

376 """Is the `other` enumerated type different from this one? 

377 

378 Two enumerated types are different if they don't have exactly 

379 the same enumerated values (i.e. with the same names and 

380 concrete values). 

381 

382 Examples 

383 -------- 

384 

385 Let ``enum*`` be enumerated types defined as: 

386 

387 >>> enum1 = Enum({'T0': 0, 'T1': 2}) 

388 >>> enum2 = Enum(enum1) 

389 >>> enum3 = Enum({'T1': 2, 'T0': 0}) 

390 >>> enum4 = Enum({'T0': 0, 'T1': 2, 'T2': 5}) 

391 >>> enum5 = Enum({'T0': 0}) 

392 >>> enum6 = Enum({'T0': 10, 'T1': 20}) 

393 

394 then: 

395 

396 >>> enum1 != enum1 

397 False 

398 >>> enum1 != enum2 != enum3 

399 False 

400 >>> enum1 != enum4 

401 True 

402 >>> enum5 != enum1 

403 True 

404 >>> enum1 != enum6 

405 True 

406 

407 """ 

408 

409 return not self.__eq__(other) 

410 

411 # XXX: API incompatible change for PyTables 3 line 

412 # Overriding __eq__ blocks inheritance of __hash__ in 3.x 

413 # def __hash__(self): 

414 # return hash((self.__class__, tuple(self._names.items()))) 

415 def __repr__(self): 

416 """Return the canonical string representation of the enumeration. The 

417 output of this method can be evaluated to give a new enumeration object 

418 that will compare equal to this one. 

419 

420 Examples 

421 -------- 

422 >>> repr(Enum({'name': 10})) 

423 "Enum({'name': 10})" 

424 

425 """ 

426 

427 return 'Enum(%s)' % self._names 

428 

429 

430def _test(): 

431 import doctest 

432 return doctest.testmod() 

433 

434 

435if __name__ == '__main__': 

436 _test()