# enum.py/Open GoPro, Version 2.0 (C) Copyright 2021 GoPro, Inc. (http://gopro.com/OpenGoPro). # This copyright was auto-generated on Mon Jul 31 17:04:07 UTC 2023 """Custom enum definition""" from __future__ import annotations from enum import Enum, EnumMeta, IntEnum from typing import Any, Iterator, Protocol, TypeVar, no_type_check T = TypeVar("T") class ProtobufDescriptor(Protocol): """Protocol definition for Protobuf enum descriptor used to generate GoPro enums from protobufs""" @property def name(self) -> str: """Human readable name of protobuf enum Returns: str: enum name """ @property def values_by_name(self) -> dict: """Get the enum values by name Returns: dict: Dict of enum values mapped by name """ @property def values_by_number(self) -> dict: """Get the enum values by number Returns: dict: dict of enum numbers mapped by number """ class GoProEnumMeta(EnumMeta): """Modify enum metaclass to build GoPro specific enums""" _is_proto = False _iter_skip_names = ("NOT_APPLICABLE", "DESCRIPTOR") @no_type_check def __new__(mcs, name, bases, classdict, **kwargs) -> GoProEnumMeta: # noqa is_proto = "__is_proto__" in classdict classdict["_ignore_"] = "__is_proto__" classdict["__doc__"] = "" # Don't use useless "An enumeration" docstring e = super().__new__(mcs, name, bases, classdict, **kwargs) setattr(e, "_is_proto", is_proto) return e @no_type_check def __contains__(cls: type[Any], obj: object) -> bool: if isinstance(obj, Enum): return super().__contains__(obj) if isinstance(obj, int): return obj in [x.value for x in cls._member_map_.values()] if isinstance(obj, str): return obj.lower() in [x.name.lower() for x in cls._member_map_.values()] raise TypeError( f"unsupported operand type(s) for 'in': {type(obj).__qualname__} and {cls.__class__.__qualname__}" ) def __iter__(cls: type[T]) -> Iterator[T]: """Do not return enum values whose name is in the _iter_skip_names list Returns: Iterator[T]: enum iterator """ return iter([x[1] for x in cls._member_map_.items() if x[0] not in GoProEnumMeta._iter_skip_names]) # type: ignore class GoProIntEnum(IntEnum, metaclass=GoProEnumMeta): """GoPro specific integer enum to be used for all settings, statuses, and parameters The names NOT_APPLICABLE and DESCRIPTOR are special as they will not be returned as part of the enum iterator """ def __eq__(self, other: object) -> bool: if type(self)._is_proto: if isinstance(other, int): return self.value == other if isinstance(other, str): return self.name == other if isinstance(other, Enum): return self.value == other.value raise TypeError(f"Unexpected case: proto enum can only be str or int, not {type(other)}") return super(IntEnum, self).__eq__(other) def __hash__(self) -> Any: return hash(self.name + str(self.value)) def __str__(self) -> str: return super(IntEnum, self).__str__() class GoProEnum(Enum, metaclass=GoProEnumMeta): """GoPro specific enum to be used for all settings, statuses, and parameters The names NOT_APPLICABLE and DESCRIPTOR are special as they will not be returned as part of the enum iterator """ def __eq__(self, other: object) -> bool: if type(self)._is_proto: if isinstance(other, int): return self.value == other if isinstance(other, str): return self.name == other if isinstance(other, Enum): return self.value == other.value raise TypeError(f"Unexpected case: proto enum can only be str or int, not {type(other)}") return super().__eq__(other) def __hash__(self) -> Any: return hash(self.name + str(self.value)) def enum_factory(proto_enum: ProtobufDescriptor) -> type[GoProIntEnum]: """Dynamically build a GoProEnum from a protobuf enum Args: proto_enum (ProtobufDescriptor): input protobuf enum descriptor Returns: type[GoProIntEnum]: generated GoProEnum """ keys = proto_enum.values_by_name.keys() values = list(proto_enum.values_by_number.keys()) # This has somehow changed between protobuf versions if isinstance(proto_enum.values_by_number, dict): values.reverse() return GoProIntEnum( # type: ignore # pylint: disable=too-many-function-args proto_enum.name, # type: ignore { **dict(zip(keys, values)), "__is_proto__": True, }, )