1
1
"""Base class for all enumerations."""
2
+ from deprecation import deprecated
2
3
from enum import Enum
4
+ from typing import Optional
3
5
4
6
5
- class BaseEnumeration (Enum ):
6
- """Enumeration class that can convert between enumerations and associated values."""
7
+ class BaseEnumeration (str , Enum ):
8
+ """
9
+ Enumeration class that can convert between enumerations and associated values.
7
10
8
- def __init__ (self , * _ ):
9
- """Ensure that there are no duplicates in the enumeration."""
10
- cls = self .__class__
11
- if any (self .value == e .value for e in cls ):
12
- raise ValueError ("Duplicates not allowed in enumerated set of values {}" .format (cls ))
13
- if not isinstance (self .value , str ):
11
+ BaseEnumeration is a powerful support class for string enumerations. It inherits
12
+ from both str and Enum to enable a class with str capabilities but still a
13
+ restricted data space. All constructors are case-insensitive on input and a given
14
+ enumeration can recognize multiple synonyms for input, though only one value will
15
+ correspond to the value itsself. For example:
16
+
17
+ ```
18
+ Fruits(BaseEnumeration):
19
+ APPLE = "Apple"
20
+ AVOCADO = "Avocado", "Alligator Pear"
21
+ ```
22
+
23
+ will recognize "apple", "APPLE" and " aPpLe " as referring to Fruits.APPLE,
24
+ and "avocado" and "alligator pear" as referring to Fruits.AVOCADO. However,
25
+ since str(Fruits.AVOCADO) is "Avocado", Fruits.AVOCADO != "Alligator Pear".
26
+
27
+ """
28
+
29
+ def __new__ (cls , value : str , * args ):
30
+ """Overloaded to allow for synonyms."""
31
+ if any (not isinstance (x , str ) for x in (value ,) + args ):
14
32
raise ValueError ("All values of enum {} must be strings" .format (cls ))
33
+ if cls .from_str (value , exception = False ) is not None :
34
+ raise ValueError ("Duplicates not allowed in enumerated set of values {}" .format (cls ))
35
+ obj = str .__new__ (cls , value )
36
+ obj ._value_ = value
37
+ obj .synonyms = frozenset (args )
38
+ obj .matches = frozenset ([obj .lower ()]).union (x .lower () for x in obj .synonyms )
39
+ return obj
15
40
16
41
@classmethod
17
- def get_value (cls , name ):
42
+ def from_str (cls , val : str , * , exception : bool = False ) -> Optional ["BaseEnumeration" ]:
43
+ """
44
+ Given a string value, return the Enumeration object that matches.
45
+
46
+ Parameters
47
+ ----------
48
+ val: str
49
+ The string to match against. Leading and trailing whitespace is ignored.
50
+ Case is ignored.
51
+ exception: bool
52
+ Whether to raise an error if the string doesn't match anything. Default: False.
53
+
54
+ Returns
55
+ -------
56
+ BaseEnumeration
57
+ The matching enumerated element, or None
58
+
59
+ :param val:
60
+ :param exception:
61
+ :return:
62
+
63
+ """
64
+ if val is None :
65
+ result = None
66
+ else :
67
+ result = next ((x for x in cls if str .lower (val ).strip () in x .matches ), None )
68
+ if exception and result is None :
69
+ raise ValueError (f"{ val } is not a valid { cls } ; valid choices are { [x for x in cls ]} " )
70
+ return result
71
+
72
+ @classmethod
73
+ @deprecated (deprecated_in = "1.15.0" ,
74
+ removed_in = "2.0.0" ,
75
+ details = "Enumerations autocast to values now." )
76
+ def get_value (cls , name : str ) -> str :
18
77
"""
19
78
Return a valid value associated with name.
20
79
@@ -23,14 +82,13 @@ def get_value(cls, name):
23
82
"""
24
83
if name is None :
25
84
return None
26
- if any (name == e .value for e in cls ):
27
- return name
28
- if any (name == e for e in cls ):
29
- return name .value
30
- raise ValueError ("'{}' is not a valid choice for enumeration {}" .format (name , cls ))
85
+ return cls .from_str (name , exception = True ).value
31
86
32
87
@classmethod
33
- def get_enum (cls , name ):
88
+ @deprecated (deprecated_in = "1.15.0" ,
89
+ removed_in = "2.0.0" ,
90
+ details = "Use from_str for retreiving the correct Enum object." )
91
+ def get_enum (cls , name : str ) -> "BaseEnumeration" :
34
92
"""
35
93
Return the enumeration associated with name.
36
94
@@ -39,8 +97,8 @@ def get_enum(cls, name):
39
97
"""
40
98
if name is None :
41
99
return None
42
- if any (name == e . value for e in cls ):
43
- return next ( e for e in cls if e . value == name )
44
- if any ( name == e for e in cls ):
45
- return name
46
- raise ValueError ( "'{}' is not a valid choice for enumeration {}" . format ( name , cls ))
100
+ return cls . from_str (name , exception = True )
101
+
102
+ def __str__ ( self ):
103
+ """Return the value of the enumeration object."""
104
+ return self . value
0 commit comments