-
Notifications
You must be signed in to change notification settings - Fork 72
/
Copy pathapiattr.py
185 lines (144 loc) · 5.41 KB
/
apiattr.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
class ApiAttribute:
"""A data descriptor that sets and returns values."""
def __init__(self, name):
"""Create an instance of ApiAttribute.
:param name: name of this attribute.
:type name: str.
"""
self.name = name
def __get__(self, obj, type=None):
"""Accesses value of this attribute."""
return obj.attr.get(self.name)
def __set__(self, obj, value):
"""Write value of this attribute."""
obj.attr[self.name] = value
if obj.dirty.get(self.name) is not None:
obj.dirty[self.name] = True
def __del__(self, obj=None):
"""Delete value of this attribute."""
if not obj:
return
del obj.attr[self.name]
if obj.dirty.get(self.name) is not None:
del obj.dirty[self.name]
class ApiAttributeMixin:
"""Mixin to initialize required global variables to use ApiAttribute."""
def __init__(self):
self.attr = {}
self.dirty = {}
self.http = None # Any element may make requests and will require this
# field.
class ApiResource(dict):
"""Super class of all api resources.
Inherits and behaves as a python dictionary to handle api resources.
Save clean copy of metadata in self.metadata as a dictionary.
Provides changed metadata elements to efficiently update api resources.
"""
auth = ApiAttribute("auth")
def __init__(self, *args, **kwargs):
"""Create an instance of ApiResource."""
super().__init__()
self.update(*args, **kwargs)
self.metadata = dict(self)
def __getitem__(self, key):
"""Overwritten method of dictionary.
:param key: key of the query.
:type key: str.
:returns: value of the query.
"""
return dict.__getitem__(self, key)
def __setitem__(self, key, val):
"""Overwritten method of dictionary.
:param key: key of the query.
:type key: str.
:param val: value of the query.
"""
dict.__setitem__(self, key, val)
def __repr__(self):
"""Overwritten method of dictionary."""
dict_representation = dict.__repr__(self)
return f"{type(self).__name__}({dict_representation})"
def update(self, *args, **kwargs):
"""Overwritten method of dictionary."""
BAD_URL_PREFIX = "https://www.googleapis.comhttps:"
for k, v in dict(*args, **kwargs).items():
if k == "downloadUrl" and v.startswith(BAD_URL_PREFIX):
v = v.replace(BAD_URL_PREFIX, "https://www.googleapis.com", 1)
self[k] = v
def UpdateMetadata(self, metadata=None):
"""Update metadata and mark all of them to be clean."""
if metadata:
self.update(metadata)
self.metadata = dict(self)
def GetChanges(self):
"""Returns changed metadata elements to update api resources efficiently.
:returns: dict -- changed metadata elements.
"""
dirty = {}
for key in self:
if self.metadata.get(key) is None:
dirty[key] = self[key]
elif self.metadata[key] != self[key]:
dirty[key] = self[key]
return dirty
class ApiResourceList(ApiAttributeMixin, ApiResource):
"""Abstract class of all api list resources.
Inherits ApiResource and builds iterator to list any API resource.
"""
metadata = ApiAttribute("metadata")
def __init__(self, auth=None, metadata=None):
"""Create an instance of ApiResourceList.
:param auth: authorized GoogleAuth instance.
:type auth: GoogleAuth.
:param metadata: parameter to send to list command.
:type metadata: dict.
"""
ApiAttributeMixin.__init__(self)
ApiResource.__init__(self)
self.auth = auth
self.UpdateMetadata()
if metadata:
self.update(metadata)
def __iter__(self):
"""Returns iterator object.
:returns: ApiResourceList -- self
"""
return self
def __next__(self):
"""Make API call to list resources and return them.
Auto updates 'pageToken' every time it makes API call and
raises StopIteration when it reached the end of iteration.
:returns: list -- list of API resources.
:raises: StopIteration
"""
if "pageToken" in self and self["pageToken"] is None:
raise StopIteration
result = self._GetList()
self["pageToken"] = self.metadata.get("nextPageToken")
return result
def GetList(self):
"""Get list of API resources.
If 'maxResults' is not specified, it will automatically iterate through
every resources available. Otherwise, it will make API call once and
update 'pageToken'.
:returns: list -- list of API resources.
"""
if self.get("maxResults") is None:
self["maxResults"] = 1000
result = []
for x in self:
result.extend(x)
del self["maxResults"]
return result
else:
return next(self)
def _GetList(self):
"""Helper function which actually makes API call.
Should be overwritten.
:raises: NotImplementedError
"""
raise NotImplementedError
def Reset(self):
"""Resets current iteration"""
if "pageToken" in self:
del self["pageToken"]