enum.py 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143
  1. # enum.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro).
  2. # This copyright was auto-generated on Mon Jul 31 17:04:07 UTC 2023
  3. """Custom enum definition"""
  4. from __future__ import annotations
  5. from enum import Enum, EnumMeta, IntEnum
  6. from typing import Any, Iterator, Protocol, TypeVar, no_type_check
  7. T = TypeVar("T")
  8. class ProtobufDescriptor(Protocol):
  9. """Protocol definition for Protobuf enum descriptor used to generate GoPro enums from protobufs"""
  10. @property
  11. def name(self) -> str:
  12. """Human readable name of protobuf enum
  13. Returns:
  14. str: enum name
  15. """
  16. @property
  17. def values_by_name(self) -> dict:
  18. """Get the enum values by name
  19. Returns:
  20. dict: Dict of enum values mapped by name
  21. """
  22. @property
  23. def values_by_number(self) -> dict:
  24. """Get the enum values by number
  25. Returns:
  26. dict: dict of enum numbers mapped by number
  27. """
  28. class GoProEnumMeta(EnumMeta):
  29. """Modify enum metaclass to build GoPro specific enums"""
  30. _is_proto = False
  31. _iter_skip_names = ("NOT_APPLICABLE", "DESCRIPTOR")
  32. @no_type_check
  33. def __new__(mcs, name, bases, classdict, **kwargs) -> GoProEnumMeta: # noqa
  34. is_proto = "__is_proto__" in classdict
  35. classdict["_ignore_"] = "__is_proto__"
  36. classdict["__doc__"] = "" # Don't use useless "An enumeration" docstring
  37. e = super().__new__(mcs, name, bases, classdict, **kwargs)
  38. setattr(e, "_is_proto", is_proto)
  39. return e
  40. @no_type_check
  41. def __contains__(cls: type[Any], obj: object) -> bool:
  42. if isinstance(obj, Enum):
  43. return super().__contains__(obj)
  44. if isinstance(obj, int):
  45. return obj in [x.value for x in cls._member_map_.values()]
  46. if isinstance(obj, str):
  47. return obj.lower() in [x.name.lower() for x in cls._member_map_.values()]
  48. raise TypeError(
  49. f"unsupported operand type(s) for 'in': {type(obj).__qualname__} and {cls.__class__.__qualname__}"
  50. )
  51. def __iter__(cls: type[T]) -> Iterator[T]:
  52. """Do not return enum values whose name is in the _iter_skip_names list
  53. Returns:
  54. Iterator[T]: enum iterator
  55. """
  56. return iter([x[1] for x in cls._member_map_.items() if x[0] not in GoProEnumMeta._iter_skip_names]) # type: ignore
  57. class GoProIntEnum(IntEnum, metaclass=GoProEnumMeta):
  58. """GoPro specific integer enum to be used for all settings, statuses, and parameters
  59. The names NOT_APPLICABLE and DESCRIPTOR are special as they will not be returned as part of the enum iterator
  60. """
  61. def __eq__(self, other: object) -> bool:
  62. if type(self)._is_proto:
  63. if isinstance(other, int):
  64. return self.value == other
  65. if isinstance(other, str):
  66. return self.name == other
  67. if isinstance(other, Enum):
  68. return self.value == other.value
  69. raise TypeError(f"Unexpected case: proto enum can only be str or int, not {type(other)}")
  70. return super(IntEnum, self).__eq__(other)
  71. def __hash__(self) -> Any:
  72. return hash(self.name + str(self.value))
  73. def __str__(self) -> str:
  74. return super(IntEnum, self).__str__()
  75. class GoProEnum(Enum, metaclass=GoProEnumMeta):
  76. """GoPro specific enum to be used for all settings, statuses, and parameters
  77. The names NOT_APPLICABLE and DESCRIPTOR are special as they will not be returned as part of the enum iterator
  78. """
  79. def __eq__(self, other: object) -> bool:
  80. if type(self)._is_proto:
  81. if isinstance(other, int):
  82. return self.value == other
  83. if isinstance(other, str):
  84. return self.name == other
  85. if isinstance(other, Enum):
  86. return self.value == other.value
  87. raise TypeError(f"Unexpected case: proto enum can only be str or int, not {type(other)}")
  88. return super().__eq__(other)
  89. def __hash__(self) -> Any:
  90. return hash(self.name + str(self.value))
  91. def enum_factory(proto_enum: ProtobufDescriptor) -> type[GoProIntEnum]:
  92. """Dynamically build a GoProEnum from a protobuf enum
  93. Args:
  94. proto_enum (ProtobufDescriptor): input protobuf enum descriptor
  95. Returns:
  96. type[GoProIntEnum]: generated GoProEnum
  97. """
  98. keys = proto_enum.values_by_name.keys()
  99. values = list(proto_enum.values_by_number.keys())
  100. # This has somehow changed between protobuf versions
  101. if isinstance(proto_enum.values_by_number, dict):
  102. values.reverse()
  103. return GoProIntEnum( # type: ignore # pylint: disable=too-many-function-args
  104. proto_enum.name, # type: ignore
  105. {
  106. **dict(zip(keys, values)),
  107. "__is_proto__": True,
  108. },
  109. )