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
« prev ^ index » next coverage.py v7.2.5, created at 2023-05-10 06:15 +0000
1"""Implementation of enumerated types.
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.
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.
17"""
20__docformat__ = 'reStructuredText'
21"""The format of documentation strings in this module."""
24class Enum:
25 """Enumerated type.
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.
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:
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::
43 >>> boolEnum = Enum(['True', 'False'])
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::
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
56 - *Enumerated type*: in that case, a copy
57 of the original enumerated type is created. Both enumerated
58 types are considered equal::
60 >>> prio2 = Enum(priority)
61 >>> priority == prio2
62 True
64 Please note that names starting with _ are
65 not allowed, since they are reserved for internal usage::
67 >>> prio2 = Enum(['_xx'])
68 Traceback (most recent call last):
69 ...
70 ValueError: name of enumerated value can not start with ``_``: '_xx'
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::
79 >>> redv = priority.red
80 >>> redv == priority['red']
81 True
82 >>> redv > priority.green
83 True
84 >>> priority.red == priority.orange
85 False
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__()::
93 >>> priority(redv)
94 'red'
95 >>> priority.red == priority[priority(priority.red)]
96 True
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.)
102 """
104 def __init__(self, enum):
105 mydict = self.__dict__
107 mydict['_names'] = {}
108 mydict['_values'] = {}
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""")
124 def _check_and_set_pair(self, name, value):
125 """Check validity of enumerated value and insert it into type."""
127 names = self._names
128 values = self._values
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)
147 names[name] = value
148 values[value] = name
149 self.__dict__[name] = value
151 def __getitem__(self, name):
152 """Get the concrete value of the enumerated value with that name.
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.
157 Examples
158 --------
160 Let ``enum`` be an enumerated type defined as:
162 >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5})
164 then:
166 >>> enum['T1']
167 2
168 >>> enum['foo']
169 Traceback (most recent call last):
170 ...
171 KeyError: "no enumerated value with that name: 'foo'"
173 """
175 try:
176 return self._names[name]
177 except KeyError:
178 raise KeyError(f"no enumerated value with that name: {name!r}")
180 def __setitem__(self, name, value):
181 """This operation is forbidden."""
182 raise IndexError("operation not allowed")
184 def __delitem__(self, name):
185 """This operation is forbidden."""
186 raise IndexError("operation not allowed")
188 def __getattr__(self, name):
189 """Get the concrete value of the enumerated value with that name.
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.
194 Examples
195 --------
196 Let ``enum`` be an enumerated type defined as:
198 >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5})
200 then:
202 >>> enum.T1
203 2
204 >>> enum.foo
205 Traceback (most recent call last):
206 ...
207 AttributeError: no enumerated value with that name: 'foo'
209 """
211 try:
212 return self[name]
213 except KeyError as ke:
214 raise AttributeError(*ke.args)
216 def __setattr__(self, name, value):
217 """This operation is forbidden."""
218 raise AttributeError("operation not allowed")
220 def __delattr__(self, name):
221 """This operation is forbidden."""
222 raise AttributeError("operation not allowed")
224 def __contains__(self, name):
225 """Is there an enumerated value with that name in the type?
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.
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.
233 Examples
234 --------
235 Let ``enum`` be an enumerated type defined as:
237 >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5})
239 then:
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
254 """
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
261 def __call__(self, value, *default):
262 """Get the name of the enumerated value with that concrete value.
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.
268 This method can be used for checking that a concrete value belongs to
269 the set of concrete values in an enumerated type.
271 Examples
272 --------
273 Let ``enum`` be an enumerated type defined as:
275 >>> enum = Enum({'T0': 0, 'T1': 2, 'T2': 5})
277 then:
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
288 """
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}")
298 def __len__(self):
299 """Return the number of enumerated values in the enumerated type.
301 Examples
302 --------
303 >>> len(Enum(['e%d' % i for i in range(10)]))
304 10
306 """
308 return len(self._names)
310 def __iter__(self):
311 """Iterate over the enumerated values.
313 Enumerated values are returned as (name, value) pairs *in no particular
314 order*.
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
324 """
326 yield from self._names.items()
328 def __eq__(self, other):
329 """Is the other enumerated type equivalent to this one?
331 Two enumerated types are equivalent if they have exactly the same
332 enumerated values (i.e. with the same names and concrete values).
334 Examples
335 --------
337 Let ``enum*`` be enumerated types defined as:
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})
346 then:
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
359 Comparing enumerated types with other kinds of objects produces
360 a false result:
362 >>> enum1 == {'T0': 0, 'T1': 2}
363 False
364 >>> enum1 == ['T0', 'T1']
365 False
366 >>> enum1 == 2
367 False
369 """
371 if not isinstance(other, Enum):
372 return False
373 return self._names == other._names
375 def __ne__(self, other):
376 """Is the `other` enumerated type different from this one?
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).
382 Examples
383 --------
385 Let ``enum*`` be enumerated types defined as:
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})
394 then:
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
407 """
409 return not self.__eq__(other)
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.
420 Examples
421 --------
422 >>> repr(Enum({'name': 10}))
423 "Enum({'name': 10})"
425 """
427 return 'Enum(%s)' % self._names
430def _test():
431 import doctest
432 return doctest.testmod()
435if __name__ == '__main__':
436 _test()