feat(ipsec): Use strings instead of enums in Robot
[csit.git] / resources / libraries / python / enum_util.py
1 # Copyright (c) 2024 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
5 #
6 #     http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
13
14 """Utility functions for handling VPP API enum values from Robot."""
15
16
17 from enum import Enum, IntEnum
18 from typing import Type, Union
19
20
21 # The return type is enum_class, but it is hard to explain that to pylint.
22 def get_enum_instance(
23     enum_class: Type[Enum], value: Union[Enum, str, int, None]
24 ) -> Enum:
25     """Return an enum instance matching the string name.
26
27     In Robot, it is not convenient to construct Enum instances,
28     most values defined in Robot are strings.
29
30     This helper function can be used in Python L1 keywords
31     to convert string into the corresponding Enum instance.
32     Aliases are also recognized.
33
34     As an added benefit, support various Robot-like niceties,
35     like lower case, or dash or space instead of underscore.
36
37     As a common shortcut, value is returned it it already is an instance.
38
39     Another convenience: None or empty string is processed as "NONE".
40
41     If the class is a subclass of IntEnum, int values
42     and (string) values convertable to int are also accepted as input.
43
44     :param enum_class: Class object instance of which should be returned.
45     :param value: String or any other recognized form of an enum instance.
46     :type enum_class: Type[Enum]
47     :type value: Union[enum_class, str, int, None]
48     :returns: The matching instance, if found.
49     :rtype: enum_class
50     :raises: ValueError if no matching instance is found.
51     """
52     if issubclass(enum_class, IntEnum):
53         try:
54             int_value = int(value)
55             return enum_class(int_value)
56         except (TypeError, ValueError):
57             pass
58     if isinstance(value, enum_class):
59         return value
60     if not value:
61         value = "NONE"
62     normalized_name = str(value).upper().replace("-", "_").replace(" ", "_")
63     members = enum_class.__members__  # Includes aliases, useful for NONE.
64     if normalized_name not in members:
65         msg = f"Enum class {enum_class} does not have value {normalized_name!r}"
66         raise ValueError(msg)
67     return members[normalized_name]