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
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
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.
23"""Base model for all generated models
24"""
26import collections
27import datetime
28import enum
29import functools
30import inspect
31import keyword
32from typing import Any, Iterable, Optional, Sequence, TypeVar, cast
34import cattr
36from looker_sdk.rtl import hooks
38try:
39 from typing import ForwardRef # type: ignore
40except ImportError:
41 from typing import _ForwardRef as ForwardRef # type: ignore
44EXPLICIT_NULL = cast(Any, "EXPLICIT_NULL") # type:ignore
47class Model:
48 """Base model for all generated models."""
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
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
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)
76 if isinstance(ret, enum.Enum):
77 ret = ret.value
78 return ret
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):
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 )
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)
114 return setattr(self, key, value)
116 def __delitem__(self, key):
117 self[key] # validates key
118 setattr(self, self._key_to_attr(key), None)
120 def __iter__(self):
121 return iter(self._get_converter().unstructure(self))
123 def __len__(self):
124 return len(self._get_converter().unstructure(self))
126 def __contains__(self, key):
127 return key in self._get_converter().unstructure(self)
129 def keys(self):
130 return self._get_converter().unstructure(self).keys()
132 def items(self):
133 return self._get_converter().unstructure(self).items()
135 def values(self):
136 return self._get_converter().unstructure(self).values()
138 def get(self, key, default=None):
139 try:
140 return self[key]
141 except KeyError:
142 return default
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
150 def popitem(self):
151 raise NotImplementedError()
153 def clear(self):
154 raise NotImplementedError()
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]
168 def setdefault(self, key, default=None):
169 if key not in self:
170 self[key] = default
171 return self[key]
173 def copy(self):
174 raise NotImplementedError()
177def safe_enum__new__(cls, value):
178 """Handle out-of-spec enum values returned by API.
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)
191T = TypeVar("T")
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
206 super().__init__(data)
208 def append(self, elem: T):
209 super().append(elem)
211 def extend(self, iterable: Iterable[T]):
212 super().extend(iterable)
214 def insert(self, i: int, elem: T):
215 super().insert(i, elem)
217 def remove(self, elem: T):
218 super().remove(elem)
220 def index(self, x: T, *args):
221 super().index(x, *args) # type: ignore
223 def count(self, elem: T):
224 super().count(elem)
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 )
233class URLSearchParams(dict):
234 pass