Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/looker_sdk/rtl/model.py: 33%

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

136 statements  

1# The MIT License (MIT) 

2# 

3# Copyright (c) 2019 Looker Data Sciences, Inc. 

4# 

5# Permission is hereby granted, free of charge, to any person obtaining a copy 

6# of this software and associated documentation files (the "Software"), to deal 

7# in the Software without restriction, including without limitation the rights 

8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 

9# copies of the Software, and to permit persons to whom the Software is 

10# furnished to do so, subject to the following conditions: 

11# 

12# The above copyright notice and this permission notice shall be included in 

13# all copies or substantial portions of the Software. 

14# 

15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 

21# THE SOFTWARE. 

22 

23"""Base model for all generated models 

24""" 

25 

26import collections 

27import datetime 

28import enum 

29import functools 

30import inspect 

31import keyword 

32from typing import Any, Iterable, Optional, Sequence, TypeVar, cast 

33 

34import cattr 

35 

36from looker_sdk.rtl import hooks 

37 

38try: 

39 from typing import ForwardRef # type: ignore 

40except ImportError: 

41 from typing import _ForwardRef as ForwardRef # type: ignore 

42 

43 

44EXPLICIT_NULL = cast(Any, "EXPLICIT_NULL") # type:ignore 

45 

46 

47class Model: 

48 """Base model for all generated models.""" 

49 

50 def _get_converter(self): 

51 if not hasattr(self, "_converter"): 

52 converter = cattr.Converter() 

53 converter.register_unstructure_hook( 

54 datetime.datetime, hooks.datetime_unstructure_hook 

55 ) 

56 uh = functools.partial(hooks.unstructure_hook, converter) 

57 converter.register_unstructure_hook(Model, uh) # type: ignore 

58 self._converter = converter 

59 return self._converter 

60 

61 def _key_to_attr(self, key): 

62 """Appends the trailing _ to python reserved words.""" 

63 if key[-1] == "_": 

64 raise KeyError(key) 

65 if key in keyword.kwlist: 

66 key = f"{key}_" 

67 return key 

68 

69 def __getitem__(self, key): 

70 key = self._key_to_attr(key) 

71 try: 

72 ret = getattr(self, key) 

73 except AttributeError: 

74 raise KeyError(key) 

75 

76 if isinstance(ret, enum.Enum): 

77 ret = ret.value 

78 return ret 

79 

80 def __setitem__(self, key, value): 

81 key = self._key_to_attr(key) 

82 if not hasattr(self, key): 

83 raise AttributeError( 

84 f"'{self.__class__.__name__}' object has no attribute '{key}'" 

85 ) 

86 annotation = inspect.get_annotations(self.__class__)[key] 

87 if isinstance(annotation, ForwardRef): 

88 actual_type = eval( 

89 annotation.__forward_arg__, self.__global_context, locals() 

90 ) 

91 if isinstance(actual_type, enum.EnumMeta): 

92 

93 # untyped because mypy really doesn't like this enum internals stuff 

94 def err(val): 

95 valid = [] 

96 for v in actual_type.__members__.values(): 

97 if v.value != "invalid_api_enum_value": 

98 valid.append(v.value) 

99 return ( 

100 f"Invalid value '{val}' for " # type: ignore 

101 f"'{self.__class__.__name__}.{key}'. Valid values are " 

102 f"{valid}" # type: ignore 

103 ) 

104 

105 if isinstance(value, actual_type): 

106 raise ValueError(err(value)) 

107 enum_member = actual_type(value) 

108 if enum_member.value == "invalid_api_enum_value": 

109 raise ValueError(err(value)) 

110 value = enum_member 

111 elif issubclass(actual_type, Model): 

112 value = self._get_converter().structure(value, actual_type) 

113 

114 return setattr(self, key, value) 

115 

116 def __delitem__(self, key): 

117 self[key] # validates key 

118 setattr(self, self._key_to_attr(key), None) 

119 

120 def __iter__(self): 

121 return iter(self._get_converter().unstructure(self)) 

122 

123 def __len__(self): 

124 return len(self._get_converter().unstructure(self)) 

125 

126 def __contains__(self, key): 

127 return key in self._get_converter().unstructure(self) 

128 

129 def keys(self): 

130 return self._get_converter().unstructure(self).keys() 

131 

132 def items(self): 

133 return self._get_converter().unstructure(self).items() 

134 

135 def values(self): 

136 return self._get_converter().unstructure(self).values() 

137 

138 def get(self, key, default=None): 

139 try: 

140 return self[key] 

141 except KeyError: 

142 return default 

143 

144 def pop(self, key, default=None): 

145 ret = self.get(key, default) 

146 if key in self: 

147 del self[key] 

148 return ret 

149 

150 def popitem(self): 

151 raise NotImplementedError() 

152 

153 def clear(self): 

154 raise NotImplementedError() 

155 

156 def update(self, iterable=None, **kwargs): 

157 if iterable: 

158 has_keys = getattr(iterable, "keys", None) 

159 if callable(has_keys): 

160 for k in iterable: 

161 self[k] = iterable[k] 

162 else: 

163 for k, v in iterable: 

164 self[k] = v 

165 for k in kwargs: 

166 self[k] = kwargs[k] 

167 

168 def setdefault(self, key, default=None): 

169 if key not in self: 

170 self[key] = default 

171 return self[key] 

172 

173 def copy(self): 

174 raise NotImplementedError() 

175 

176 

177def safe_enum__new__(cls, value): 

178 """Handle out-of-spec enum values returned by API. 

179 

180 This is achieved by overriding the __new__ method to return 

181 `invalid_api_enum_value` (defined on each subclass) when an 

182 unexpected value for the enum is returned by the API. 

183 """ 

184 if not isinstance(value, (str, int, bool)): 

185 return super().__new__(cls, value) 

186 else: 

187 vals = {v.value: v for v in cls.__members__.values()} 

188 return vals.get(value, cls.invalid_api_enum_value) 

189 

190 

191T = TypeVar("T") 

192 

193 

194class DelimSequence(collections.UserList, Sequence[T]): 

195 def __init__( 

196 self, 

197 data: Optional[Sequence[T]] = None, 

198 prefix: str = "", 

199 suffix: str = "", 

200 separator: str = ",", 

201 ): 

202 self.prefix = prefix 

203 self.suffix = suffix 

204 self.separator = separator 

205 

206 super().__init__(data) 

207 

208 def append(self, elem: T): 

209 super().append(elem) 

210 

211 def extend(self, iterable: Iterable[T]): 

212 super().extend(iterable) 

213 

214 def insert(self, i: int, elem: T): 

215 super().insert(i, elem) 

216 

217 def remove(self, elem: T): 

218 super().remove(elem) 

219 

220 def index(self, x: T, *args): 

221 super().index(x, *args) # type: ignore 

222 

223 def count(self, elem: T): 

224 super().count(elem) 

225 

226 def __str__(self): 

227 return ( 

228 f"{self.prefix}" 

229 f"{self.separator.join(str(d) for d in self.data)}" 

230 f"{self.suffix}" 

231 ) 

232 

233class URLSearchParams(dict): 

234 pass 

235