Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/betterproto/enum.py: 70%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

88 statements  

1from __future__ import annotations 

2 

3import sys 

4from enum import ( 

5 EnumMeta, 

6 IntEnum, 

7) 

8from types import MappingProxyType 

9from typing import ( 

10 TYPE_CHECKING, 

11 Any, 

12 Dict, 

13 Optional, 

14 Tuple, 

15) 

16 

17 

18if TYPE_CHECKING: 

19 from collections.abc import ( 

20 Generator, 

21 Mapping, 

22 ) 

23 

24 from typing_extensions import ( 

25 Never, 

26 Self, 

27 ) 

28 

29 

30def _is_descriptor(obj: object) -> bool: 

31 return ( 

32 hasattr(obj, "__get__") or hasattr(obj, "__set__") or hasattr(obj, "__delete__") 

33 ) 

34 

35 

36class EnumType(EnumMeta if TYPE_CHECKING else type): 

37 _value_map_: Mapping[int, Enum] 

38 _member_map_: Mapping[str, Enum] 

39 

40 def __new__( 

41 mcs, name: str, bases: Tuple[type, ...], namespace: Dict[str, Any] 

42 ) -> Self: 

43 value_map = {} 

44 member_map = {} 

45 

46 new_mcs = type( 

47 f"{name}Type", 

48 tuple( 

49 dict.fromkeys( 

50 [base.__class__ for base in bases if base.__class__ is not type] 

51 + [EnumType, type] 

52 ) 

53 ), # reorder the bases so EnumType and type are last to avoid conflicts 

54 {"_value_map_": value_map, "_member_map_": member_map}, 

55 ) 

56 

57 members = { 

58 name: value 

59 for name, value in namespace.items() 

60 if not _is_descriptor(value) and not name.startswith("__") 

61 } 

62 

63 cls = type.__new__( 

64 new_mcs, 

65 name, 

66 bases, 

67 {key: value for key, value in namespace.items() if key not in members}, 

68 ) 

69 # this allows us to disallow member access from other members as 

70 # members become proper class variables 

71 

72 for name, value in members.items(): 

73 member = value_map.get(value) 

74 if member is None: 

75 member = cls.__new__(cls, name=name, value=value) # type: ignore 

76 value_map[value] = member 

77 member_map[name] = member 

78 type.__setattr__(new_mcs, name, member) 

79 

80 return cls 

81 

82 if not TYPE_CHECKING: 

83 

84 def __call__(cls, value: int) -> Enum: 

85 try: 

86 return cls._value_map_[value] 

87 except (KeyError, TypeError): 

88 raise ValueError(f"{value!r} is not a valid {cls.__name__}") from None 

89 

90 def __iter__(cls) -> Generator[Enum, None, None]: 

91 yield from cls._member_map_.values() 

92 

93 if sys.version_info >= (3, 8): # 3.8 added __reversed__ to dict_values 

94 

95 def __reversed__(cls) -> Generator[Enum, None, None]: 

96 yield from reversed(cls._member_map_.values()) 

97 

98 else: 

99 

100 def __reversed__(cls) -> Generator[Enum, None, None]: 

101 yield from reversed(tuple(cls._member_map_.values())) 

102 

103 def __getitem__(cls, key: str) -> Enum: 

104 return cls._member_map_[key] 

105 

106 @property 

107 def __members__(cls) -> MappingProxyType[str, Enum]: 

108 return MappingProxyType(cls._member_map_) 

109 

110 def __repr__(cls) -> str: 

111 return f"<enum {cls.__name__!r}>" 

112 

113 def __len__(cls) -> int: 

114 return len(cls._member_map_) 

115 

116 def __setattr__(cls, name: str, value: Any) -> Never: 

117 raise AttributeError(f"{cls.__name__}: cannot reassign Enum members.") 

118 

119 def __delattr__(cls, name: str) -> Never: 

120 raise AttributeError(f"{cls.__name__}: cannot delete Enum members.") 

121 

122 def __contains__(cls, member: object) -> bool: 

123 return isinstance(member, cls) and member.name in cls._member_map_ 

124 

125 

126class Enum(IntEnum if TYPE_CHECKING else int, metaclass=EnumType): 

127 """ 

128 The base class for protobuf enumerations, all generated enumerations will 

129 inherit from this. Emulates `enum.IntEnum`. 

130 """ 

131 

132 name: Optional[str] 

133 value: int 

134 

135 if not TYPE_CHECKING: 

136 

137 def __new__(cls, *, name: Optional[str], value: int) -> Self: 

138 self = super().__new__(cls, value) 

139 super().__setattr__(self, "name", name) 

140 super().__setattr__(self, "value", value) 

141 return self 

142 

143 def __str__(self) -> str: 

144 return self.name or "None" 

145 

146 def __repr__(self) -> str: 

147 return f"{self.__class__.__name__}.{self.name}" 

148 

149 def __setattr__(self, key: str, value: Any) -> Never: 

150 raise AttributeError( 

151 f"{self.__class__.__name__} Cannot reassign a member's attributes." 

152 ) 

153 

154 def __delattr__(self, item: Any) -> Never: 

155 raise AttributeError( 

156 f"{self.__class__.__name__} Cannot delete a member's attributes." 

157 ) 

158 

159 def __copy__(self) -> Self: 

160 return self 

161 

162 def __deepcopy__(self, memo: Any) -> Self: 

163 return self 

164 

165 @classmethod 

166 def try_value(cls, value: int = 0) -> Self: 

167 """Return the value which corresponds to the value. 

168 

169 Parameters 

170 ----------- 

171 value: :class:`int` 

172 The value of the enum member to get. 

173 

174 Returns 

175 ------- 

176 :class:`Enum` 

177 The corresponding member or a new instance of the enum if 

178 ``value`` isn't actually a member. 

179 """ 

180 try: 

181 return cls._value_map_[value] 

182 except (KeyError, TypeError): 

183 return cls.__new__(cls, name=None, value=value) 

184 

185 @classmethod 

186 def from_string(cls, name: str) -> Self: 

187 """Return the value which corresponds to the string name. 

188 

189 Parameters 

190 ----------- 

191 name: :class:`str` 

192 The name of the enum member to get. 

193 

194 Raises 

195 ------- 

196 :exc:`ValueError` 

197 The member was not found in the Enum. 

198 """ 

199 try: 

200 return cls._member_map_[name] 

201 except KeyError as e: 

202 raise ValueError(f"Unknown value {name} for enum {cls.__name__}") from e