crossplane-function-pythonic 0.0.7__py3-none-any.whl → 0.0.7b0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- crossplane/pythonic/__init__.py +19 -0
- crossplane/pythonic/composite.py +670 -0
- crossplane/pythonic/function.py +260 -0
- crossplane/pythonic/main.py +187 -0
- crossplane/pythonic/packages.py +158 -0
- crossplane/pythonic/protobuf.py +941 -0
- {crossplane_function_pythonic-0.0.7.dist-info → crossplane_function_pythonic-0.0.7b0.dist-info}/METADATA +1 -1
- crossplane_function_pythonic-0.0.7b0.dist-info/RECORD +11 -0
- crossplane_function_pythonic-0.0.7b0.dist-info/entry_points.txt +2 -0
- crossplane_function_pythonic-0.0.7.dist-info/RECORD +0 -4
- {crossplane_function_pythonic-0.0.7.dist-info → crossplane_function_pythonic-0.0.7b0.dist-info}/WHEEL +0 -0
- {crossplane_function_pythonic-0.0.7.dist-info → crossplane_function_pythonic-0.0.7b0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,941 @@
|
|
1
|
+
##################################################################################
|
2
|
+
#
|
3
|
+
# Pythonic class wrappers around protobuf classes that enables traversing
|
4
|
+
# and modifying the protobuf message structure much like one can in JavaScript.
|
5
|
+
# For example, to get a region value from the composite's spec:
|
6
|
+
#
|
7
|
+
# region = request.observed.composite.resource.spec.region
|
8
|
+
#
|
9
|
+
# If any item in the path to the field does not exist an "Unknown" object is returned.
|
10
|
+
# To set a field in the composite status:
|
11
|
+
#
|
12
|
+
# response.desired.composite.resource.status.homepage.url = 'https://for.example.com'
|
13
|
+
#
|
14
|
+
# Here all items in the path to the field that do not exist will be created.
|
15
|
+
#
|
16
|
+
##################################################################################
|
17
|
+
|
18
|
+
import datetime
|
19
|
+
import google.protobuf.struct_pb2
|
20
|
+
import json
|
21
|
+
import sys
|
22
|
+
import yaml
|
23
|
+
|
24
|
+
append = sys.maxsize
|
25
|
+
|
26
|
+
|
27
|
+
def Map(**kwargs):
|
28
|
+
return Values(None, None, None, Values.Type.MAP)(**kwargs)
|
29
|
+
|
30
|
+
def List(*args):
|
31
|
+
return Values(None, None, None, Values.Type.LIST)(*args)
|
32
|
+
|
33
|
+
def Unknown():
|
34
|
+
return Values(None, None, None, Values.Type.UNKNOWN)
|
35
|
+
|
36
|
+
def Yaml(string, readOnly=None):
|
37
|
+
return _Object(yaml.safe_load(string), readOnly)
|
38
|
+
|
39
|
+
def Json(string, readOnly=None):
|
40
|
+
return _Object(json.loads(string), readOnly)
|
41
|
+
|
42
|
+
def _Object(object, readOnly=None):
|
43
|
+
if isinstance(object, dict):
|
44
|
+
values = google.protobuf.struct_pb2.Struct()
|
45
|
+
if len(object):
|
46
|
+
values.update(object)
|
47
|
+
return Values(None, None, values, Values.Type.MAP, readOnly)
|
48
|
+
if isinstance(object, (list, tuple)):
|
49
|
+
values = google.protobuf.struct_pb2.ListValue()
|
50
|
+
if len(object):
|
51
|
+
values.extend(object)
|
52
|
+
return Values(None, None, values, Values.Type.LIST, readOnly)
|
53
|
+
return object
|
54
|
+
|
55
|
+
|
56
|
+
class Message:
|
57
|
+
def __init__(self, parent, key, descriptor, message, readOnly=False):
|
58
|
+
self.__dict__['_parent'] = parent
|
59
|
+
self.__dict__['_key'] = key
|
60
|
+
self.__dict__['_descriptor'] = descriptor
|
61
|
+
self.__dict__['_message'] = message
|
62
|
+
self.__dict__['_readOnly'] = readOnly
|
63
|
+
self.__dict__['_cache'] = {}
|
64
|
+
|
65
|
+
def __getattr__(self, key):
|
66
|
+
return self[key]
|
67
|
+
|
68
|
+
def __getitem__(self, key):
|
69
|
+
if key in self._cache:
|
70
|
+
return self._cache[key]
|
71
|
+
field = self._descriptor.fields_by_name.get(key)
|
72
|
+
if not field:
|
73
|
+
raise AttributeError(obj=self, name=key)
|
74
|
+
if self._message:
|
75
|
+
value = getattr(self._message, key)
|
76
|
+
else:
|
77
|
+
value = None
|
78
|
+
if value is None and field.has_default_value:
|
79
|
+
value = field.default_value
|
80
|
+
if field.type == field.TYPE_MESSAGE:
|
81
|
+
if field.message_type.name == 'Struct':
|
82
|
+
value = Values(self, key, value, Values.Type.MAP, self._readOnly)
|
83
|
+
elif field.message_type.name == 'ListValue':
|
84
|
+
value = Values(self, key, value, Values.Type.LIST, self._readOnly)
|
85
|
+
elif field.label == field.LABEL_REPEATED:
|
86
|
+
if field.message_type.GetOptions().map_entry:
|
87
|
+
value = MapMessage(self, key, field.message_type, value, self._readOnly)
|
88
|
+
else:
|
89
|
+
value = RepeatedMessage(self, key, field.message_type, value, self._readOnly)
|
90
|
+
else:
|
91
|
+
value = Message(self, key, field.message_type, value, self._readOnly)
|
92
|
+
self._cache[key] = value
|
93
|
+
return value
|
94
|
+
|
95
|
+
def __bool__(self):
|
96
|
+
return self._message != None
|
97
|
+
|
98
|
+
def __len__(self):
|
99
|
+
return len(self._descriptor.fields)
|
100
|
+
|
101
|
+
def __contains__(self, key):
|
102
|
+
return key in self._descriptor.fields_by_name
|
103
|
+
|
104
|
+
def __iter__(self):
|
105
|
+
for key in sorted(self._descriptor.fields_by_name):
|
106
|
+
yield key, self[key]
|
107
|
+
|
108
|
+
def __hash__(self):
|
109
|
+
if self._message:
|
110
|
+
return hash(tuple(hash(item) for item in sorted(iter(self), key=lambda item: item[0])))
|
111
|
+
return 0
|
112
|
+
|
113
|
+
def __eq__(self, other):
|
114
|
+
if not isinstance(other, Message):
|
115
|
+
return False
|
116
|
+
if self._descriptor.full_name != other._descriptor.full_name:
|
117
|
+
return False
|
118
|
+
if self._message is None:
|
119
|
+
return other._message is None
|
120
|
+
elif other._message is None:
|
121
|
+
return False
|
122
|
+
if len(self) != len(other):
|
123
|
+
return False
|
124
|
+
for key, value in self:
|
125
|
+
if key not in other:
|
126
|
+
return False
|
127
|
+
if value != other[key]:
|
128
|
+
return False
|
129
|
+
return True
|
130
|
+
|
131
|
+
def __str__(self):
|
132
|
+
return format(self)
|
133
|
+
|
134
|
+
def __format__(self, spec='yaml'):
|
135
|
+
return _formatObject(self, spec)
|
136
|
+
|
137
|
+
def _fullName(self, key=None):
|
138
|
+
if self._key is not None:
|
139
|
+
if self._parent is not None:
|
140
|
+
name = self._parent._fullName(self._key)
|
141
|
+
else:
|
142
|
+
name = str(self._key)
|
143
|
+
if key is not None:
|
144
|
+
if key.isidentifier():
|
145
|
+
name += f".{key}"
|
146
|
+
else:
|
147
|
+
name += f"['{key}']"
|
148
|
+
return name
|
149
|
+
if key is not None:
|
150
|
+
return str(key)
|
151
|
+
return ''
|
152
|
+
|
153
|
+
def _create_child(self, key, type=None):
|
154
|
+
if self._readOnly:
|
155
|
+
raise ValueError(f"{self._readOnly} is read only")
|
156
|
+
if self._message is None:
|
157
|
+
self.__dict__['_message'] = self._parent._create_child(self._key)
|
158
|
+
return getattr(self._message, key)
|
159
|
+
|
160
|
+
def __call__(self, **kwargs):
|
161
|
+
if self._readOnly:
|
162
|
+
raise ValueError(f"{self._readOnly} is read only")
|
163
|
+
if self._message is None:
|
164
|
+
self.__dict__['_message'] = self._parent._create_child(self._key)
|
165
|
+
self._message.Clear()
|
166
|
+
self._cache.clear()
|
167
|
+
for key, value in kwargs.items():
|
168
|
+
self[key] = value
|
169
|
+
return self
|
170
|
+
|
171
|
+
def __setattr__(self, key, value):
|
172
|
+
self[key] = value
|
173
|
+
|
174
|
+
def __setitem__(self, key, value):
|
175
|
+
if self._readOnly:
|
176
|
+
raise ValueError(f"{self._readOnly} is read only")
|
177
|
+
if key not in self._descriptor.fields_by_name:
|
178
|
+
raise AttributeError(obj=self, name=key)
|
179
|
+
if self._message is None:
|
180
|
+
self.__dict__['_message'] = self._parent._create_child(self._key)
|
181
|
+
if isinstance(value, Message):
|
182
|
+
value = value._message
|
183
|
+
elif isinstance(value, (MapMessage, RepeatedMessage)):
|
184
|
+
value = value._messages
|
185
|
+
elif isinstance(value, Values):
|
186
|
+
value = value._values
|
187
|
+
setattr(self._message, key, value)
|
188
|
+
self._cache.pop(key, None)
|
189
|
+
|
190
|
+
def __delattr__(self, key):
|
191
|
+
del self[key]
|
192
|
+
|
193
|
+
def __delitem__(self, key):
|
194
|
+
if self._readOnly:
|
195
|
+
raise ValueError(f"{self._readOnly} is read only")
|
196
|
+
if key not in self._descriptor.fields_by_name:
|
197
|
+
raise AttributeError(obj=self, name=key)
|
198
|
+
if self._message is not None:
|
199
|
+
del self._message[key]
|
200
|
+
self._cache.pop(key, None)
|
201
|
+
|
202
|
+
|
203
|
+
class MapMessage:
|
204
|
+
def __init__(self, parent, key, descriptor, messages, readOnly=False):
|
205
|
+
self.__dict__['_parent'] = parent
|
206
|
+
self.__dict__['_key'] = key
|
207
|
+
self.__dict__['_field'] = descriptor.fields_by_name['value']
|
208
|
+
self.__dict__['_messages'] = messages
|
209
|
+
self.__dict__['_readOnly'] = readOnly
|
210
|
+
self.__dict__['_cache'] = {}
|
211
|
+
|
212
|
+
def __getattr__(self, key):
|
213
|
+
return self[key]
|
214
|
+
|
215
|
+
def __getitem__(self, key):
|
216
|
+
if key in self._cache:
|
217
|
+
return self._cache[key]
|
218
|
+
if self._messages is None or key not in self._messages:
|
219
|
+
value = None
|
220
|
+
else:
|
221
|
+
value = self._messages[key]
|
222
|
+
if value is None and self._field.has_default_value:
|
223
|
+
value = self._field.default_value
|
224
|
+
if self._field.type == self._field.TYPE_MESSAGE:
|
225
|
+
if self._field.message_type.name == 'Struct':
|
226
|
+
value = Values(self, key, value, Values.Type.MAP, self._readOnly)
|
227
|
+
elif self._field.message_type.name == 'ListValue':
|
228
|
+
value = Values(self, key, value, Values.Type.LIST, self._readOnly)
|
229
|
+
elif self._field.label == self._field.LABEL_REPEATED:
|
230
|
+
if self._field.message_type.GetOptions().map_entry:
|
231
|
+
value = MapMessage(self, key, self._field.message_type, value, self._readOnly)
|
232
|
+
else:
|
233
|
+
value = RepeatedMessage(self, key, self._field.message_type, value, self._readOnly)
|
234
|
+
else:
|
235
|
+
value = Message(self, key, self._field.message_type, value, self._readOnly)
|
236
|
+
elif self._field.type == self._field.TYPE_BYTES and isinstance(value, bytes):
|
237
|
+
value = value.decode('utf-8')
|
238
|
+
self._cache[key] = value
|
239
|
+
return value
|
240
|
+
|
241
|
+
def __bool__(self):
|
242
|
+
return self._messages != None
|
243
|
+
|
244
|
+
def __len__(self):
|
245
|
+
return 0 if self._messages is None else len(self._messages)
|
246
|
+
|
247
|
+
def __contains__(self, key):
|
248
|
+
return self._messages is not None and key in self._messages
|
249
|
+
|
250
|
+
def __iter__(self):
|
251
|
+
if self._messages is not None:
|
252
|
+
for key in sorted(self._messages):
|
253
|
+
yield key, self[key]
|
254
|
+
|
255
|
+
def __hash__(self):
|
256
|
+
if self._nessages is not None:
|
257
|
+
return hash(tuple(hash(item) for item in sorted(iter(self), key=lambda item: item[0])))
|
258
|
+
return 0
|
259
|
+
|
260
|
+
def __eq__(self, other):
|
261
|
+
if not isinstance(other, MapMessage):
|
262
|
+
return False
|
263
|
+
if self._descriptor.full_name != other._descriptor.full_name:
|
264
|
+
return False
|
265
|
+
if self._messages is None:
|
266
|
+
return other._messages is None
|
267
|
+
elif other._messages is None:
|
268
|
+
return False
|
269
|
+
if len(self) != len(other):
|
270
|
+
return False
|
271
|
+
for key, value in self:
|
272
|
+
if key not in other:
|
273
|
+
return False
|
274
|
+
if value != other[key]:
|
275
|
+
return False
|
276
|
+
return True
|
277
|
+
|
278
|
+
def __str__(self):
|
279
|
+
return format(self)
|
280
|
+
|
281
|
+
def __format__(self, spec='yaml'):
|
282
|
+
return _formatObject(self, spec)
|
283
|
+
|
284
|
+
def _fullName(self, key=None):
|
285
|
+
if self._key is not None:
|
286
|
+
if self._parent is not None:
|
287
|
+
name = self._parent._fullName(self._key)
|
288
|
+
else:
|
289
|
+
name = str(self._key)
|
290
|
+
if key is not None:
|
291
|
+
if key.isidentifier():
|
292
|
+
name += f".{key}"
|
293
|
+
else:
|
294
|
+
name += f"['{key}']"
|
295
|
+
return name
|
296
|
+
if key is not None:
|
297
|
+
return str(key)
|
298
|
+
return ''
|
299
|
+
|
300
|
+
def _create_child(self, key, type=None):
|
301
|
+
if self._readOnly:
|
302
|
+
raise ValueError(f"{self._readOnly} is read only")
|
303
|
+
if self._messages is None:
|
304
|
+
self.__dict__['_messages'] = self._parent._create_child(self._key)
|
305
|
+
return self._messages[key]
|
306
|
+
|
307
|
+
def __call__(self, **kwargs):
|
308
|
+
if self._readOnly:
|
309
|
+
raise ValueError(f"{self._readOnly} is read only")
|
310
|
+
if self._messages is None:
|
311
|
+
self.__dict__['_messages'] = self._parent._create_child(self._key)
|
312
|
+
self._messages.clear()
|
313
|
+
self._cache.clear()
|
314
|
+
for key, value in kwargs.items():
|
315
|
+
self[key] = value
|
316
|
+
return self
|
317
|
+
|
318
|
+
def __setattr__(self, key, message):
|
319
|
+
self[key] = message
|
320
|
+
|
321
|
+
def __setitem__(self, key, message):
|
322
|
+
if self._readOnly:
|
323
|
+
raise ValueError(f"{self._readOnly} is read only")
|
324
|
+
if self._messages is None:
|
325
|
+
self._messages = self._parent._create_child(self._key)
|
326
|
+
if isinstance(message, Message):
|
327
|
+
message = message._message
|
328
|
+
if self._field.type == self._field.TYPE_BYTES and isinstance(message, str):
|
329
|
+
message = message.encode('utf-8')
|
330
|
+
self._messages[key] = message
|
331
|
+
self._cache.pop(key, None)
|
332
|
+
|
333
|
+
def __delattr__(self, key):
|
334
|
+
del self[key]
|
335
|
+
|
336
|
+
def __delitem__(self, key):
|
337
|
+
if self._readOnly:
|
338
|
+
raise ValueError(f"{self._readOnly} is read only")
|
339
|
+
if self._messages is not None:
|
340
|
+
if key in self._messages:
|
341
|
+
del self._messages[key]
|
342
|
+
self._cache.pop(key, None)
|
343
|
+
|
344
|
+
|
345
|
+
class RepeatedMessage:
|
346
|
+
def __init__(self, parent, key, descriptor, messages, readOnly=False):
|
347
|
+
self._parent = parent
|
348
|
+
self._key = key
|
349
|
+
self._descriptor = descriptor
|
350
|
+
self._messages = messages
|
351
|
+
self._readOnly = readOnly
|
352
|
+
self._cache = {}
|
353
|
+
|
354
|
+
def __getitem__(self, key):
|
355
|
+
if key in self._cache:
|
356
|
+
return self._cache[key]
|
357
|
+
if self._messages is None or key >= len(self._messages):
|
358
|
+
message = None
|
359
|
+
else:
|
360
|
+
message = self._messages[key]
|
361
|
+
value = Message(self, key, self._descriptor, message, self._readOnly)
|
362
|
+
self._cache[key] = value
|
363
|
+
return value
|
364
|
+
|
365
|
+
def __bool__(self):
|
366
|
+
return self._messages != None
|
367
|
+
|
368
|
+
def __len__(self):
|
369
|
+
return 0 if self._messages is None else len(self._messages)
|
370
|
+
|
371
|
+
def __contains__(self, value):
|
372
|
+
if self._messages is not None:
|
373
|
+
for message in self:
|
374
|
+
if value == message:
|
375
|
+
return True
|
376
|
+
return False
|
377
|
+
|
378
|
+
def __iter__(self):
|
379
|
+
if self._messages is not None:
|
380
|
+
for ix in range(len(self._messages)):
|
381
|
+
yield self[ix]
|
382
|
+
|
383
|
+
def __hash__(self):
|
384
|
+
if self._messages is not None:
|
385
|
+
return hash(tuple(hash(item) for item in self))
|
386
|
+
return 0
|
387
|
+
|
388
|
+
def __eq__(self, other):
|
389
|
+
if not isinstance(other, RepeatedMessage):
|
390
|
+
return False
|
391
|
+
if self._descriptor.full_name != other._descriptor.full_name:
|
392
|
+
return False
|
393
|
+
if self._messages is None:
|
394
|
+
return other._messages is None
|
395
|
+
elif other._messages is None:
|
396
|
+
return False
|
397
|
+
if len(self) != len(other):
|
398
|
+
return False
|
399
|
+
for ix, value in enumerate(self):
|
400
|
+
if value != other[ix]:
|
401
|
+
return False
|
402
|
+
return True
|
403
|
+
|
404
|
+
def __str__(self):
|
405
|
+
return format(self)
|
406
|
+
|
407
|
+
def __format__(self, spec='yaml'):
|
408
|
+
return _formatObject(self, spec)
|
409
|
+
|
410
|
+
def _fullName(self, key=None):
|
411
|
+
if self._key is not None:
|
412
|
+
if self._parent is not None:
|
413
|
+
name = self._parent._fullName(self._key)
|
414
|
+
else:
|
415
|
+
name = str(self._key)
|
416
|
+
if key is not None:
|
417
|
+
name += f"[{key}]"
|
418
|
+
return name
|
419
|
+
if key is not None:
|
420
|
+
return str(key)
|
421
|
+
return ''
|
422
|
+
|
423
|
+
def _create_child(self, key, type=None):
|
424
|
+
if self._readOnly:
|
425
|
+
raise ValueError(f"{self._readOnly} is read only")
|
426
|
+
if self._messages is None:
|
427
|
+
self.__dict__['_messages'] = self._parent._create_child(self._key)
|
428
|
+
while key >= len(self._messages):
|
429
|
+
self._messages.add()
|
430
|
+
return self._messages[key]
|
431
|
+
|
432
|
+
def __call__(self, *args):
|
433
|
+
if self._readOnly:
|
434
|
+
raise ValueError(f"{self._readOnly} is read only")
|
435
|
+
if self._messages is None:
|
436
|
+
self.__dict__['_messages'] = self._parent._create_child(self._key)
|
437
|
+
self._messages.Clear()
|
438
|
+
self._cache.clear()
|
439
|
+
for arg in args:
|
440
|
+
self.append(arg)
|
441
|
+
return self
|
442
|
+
|
443
|
+
def __setitem__(self, key, message):
|
444
|
+
if self._readOnly:
|
445
|
+
raise ValueError(f"{self._readOnly} is read only")
|
446
|
+
if self._messages is None:
|
447
|
+
self._messages = self._parent._create_child(self._key)
|
448
|
+
if key == append:
|
449
|
+
key = len(self._messages)
|
450
|
+
elif key < 0:
|
451
|
+
key = len(self._messages) + key
|
452
|
+
while key >= len(self._messages):
|
453
|
+
self._messages.add()
|
454
|
+
if isinstance(message, Message):
|
455
|
+
message = message._message
|
456
|
+
self._messages[key] = message
|
457
|
+
self._cache.pop(key, None)
|
458
|
+
|
459
|
+
def __delitem__(self, key):
|
460
|
+
if self._readOnly:
|
461
|
+
raise ValueError(f"{self._readOnly} is read only")
|
462
|
+
if self._values is not None:
|
463
|
+
del self._values[key]
|
464
|
+
self._cache.pop(key, None)
|
465
|
+
|
466
|
+
def append(self, message=None):
|
467
|
+
if self._readOnly:
|
468
|
+
raise ValueError(f"{self._readOnly} is read only")
|
469
|
+
if self._messages is None:
|
470
|
+
self._messages = self._parent._create_child(self._key)
|
471
|
+
if message is None:
|
472
|
+
message = self._messages.add()
|
473
|
+
else:
|
474
|
+
message = self._messages.append(message)
|
475
|
+
return self[len(self._messages) - 1]
|
476
|
+
|
477
|
+
|
478
|
+
class ProtobufValue:
|
479
|
+
@property
|
480
|
+
def _protobuf_value(self):
|
481
|
+
return None
|
482
|
+
|
483
|
+
|
484
|
+
class Values:
|
485
|
+
class Type:
|
486
|
+
UNKNOWN = 0
|
487
|
+
MAP = 1
|
488
|
+
LIST = 2
|
489
|
+
|
490
|
+
def __init__(self, parent, key, values, type, readOnly=None):
|
491
|
+
self.__dict__['_parent'] = parent
|
492
|
+
self.__dict__['_key'] = key
|
493
|
+
self.__dict__['_values'] = values
|
494
|
+
self.__dict__['_type'] = type
|
495
|
+
self.__dict__['_readOnly'] = readOnly
|
496
|
+
self.__dict__['_unknowns'] = {}
|
497
|
+
self.__dict__['_cache'] = {}
|
498
|
+
|
499
|
+
def __getattr__(self, key):
|
500
|
+
return self[key]
|
501
|
+
|
502
|
+
def __getitem__(self, key):
|
503
|
+
if key in self._cache:
|
504
|
+
return self._cache[key]
|
505
|
+
if key in self._unknowns:
|
506
|
+
return self._unknowns[key]
|
507
|
+
if isinstance(key, str):
|
508
|
+
if not self._isMap:
|
509
|
+
if not self._isUnknown:
|
510
|
+
raise ValueError(f"Invalid key, must be a str for maps: {key}")
|
511
|
+
self.__dict__['_type'] = self.Type.MAP
|
512
|
+
if self._values is None or key not in self._values:
|
513
|
+
struct_value = None
|
514
|
+
else:
|
515
|
+
struct_value = self._values.fields[key]
|
516
|
+
elif isinstance(key, int):
|
517
|
+
if not self._isList:
|
518
|
+
if not self._isUnknown:
|
519
|
+
raise ValueError(f"Invalid key, must be an int for lists: {key}")
|
520
|
+
self.__dict__['_type'] = self.Type.LIST
|
521
|
+
if self._values is None or key >= len(self._values):
|
522
|
+
struct_value = None
|
523
|
+
else:
|
524
|
+
struct_value = self._values.values[key]
|
525
|
+
else:
|
526
|
+
raise ValueError('Unexpected key type')
|
527
|
+
if struct_value is None:
|
528
|
+
value = Values(self, key, None, self.Type.UNKNOWN, self._readOnly)
|
529
|
+
else:
|
530
|
+
kind = struct_value.WhichOneof('kind')
|
531
|
+
if kind is None:
|
532
|
+
value = Values(self, key, None, self.Type.UNKNOWN, self._readOnly)
|
533
|
+
elif kind == 'struct_value':
|
534
|
+
value = Values(self, key, struct_value.struct_value, self.Type.MAP, self._readOnly)
|
535
|
+
elif kind == 'list_value':
|
536
|
+
value = Values(self, key, struct_value.list_value, self.Type.LIST, self._readOnly)
|
537
|
+
elif kind == 'string_value':
|
538
|
+
value = struct_value.string_value
|
539
|
+
elif kind == 'number_value':
|
540
|
+
value = struct_value.number_value
|
541
|
+
if value.is_integer():
|
542
|
+
value = int(value)
|
543
|
+
elif kind == 'bool_value':
|
544
|
+
value = struct_value.bool_value
|
545
|
+
elif kind == 'null_value':
|
546
|
+
value = None
|
547
|
+
else:
|
548
|
+
raise ValueError(f"Unexpected value kind: {kind}")
|
549
|
+
self._cache[key] = value
|
550
|
+
return value
|
551
|
+
|
552
|
+
def __bool__(self):
|
553
|
+
return self._values != None
|
554
|
+
|
555
|
+
def __len__(self):
|
556
|
+
return 0 if self._values is None else len(self._values) + len(self._unknowns)
|
557
|
+
|
558
|
+
def __contains__(self, item):
|
559
|
+
if self._values is not None:
|
560
|
+
if self._isMap:
|
561
|
+
return item in self._values or item in self._unknowns
|
562
|
+
if self._isList:
|
563
|
+
for value in self:
|
564
|
+
if item == value:
|
565
|
+
return True
|
566
|
+
return False
|
567
|
+
|
568
|
+
def __iter__(self):
|
569
|
+
if self._values is not None:
|
570
|
+
if self._isMap:
|
571
|
+
for key in sorted(set(self._values) | set(self._unknowns.keys())):
|
572
|
+
yield key, self[key]
|
573
|
+
elif self._isList:
|
574
|
+
for ix in range(len(self._values)):
|
575
|
+
yield self[ix]
|
576
|
+
for ix in sorted(self._unknowns.keys()):
|
577
|
+
if ix >= len(self._values):
|
578
|
+
yield self[ix]
|
579
|
+
|
580
|
+
def __hash__(self):
|
581
|
+
if self._values is not None:
|
582
|
+
if self._isMap:
|
583
|
+
return hash(tuple(hash(item) for item in sorted(iter(self), key=lambda item: item[0])))
|
584
|
+
if self._isList:
|
585
|
+
return hash(tuple(hash(item) for item in self))
|
586
|
+
return self._type
|
587
|
+
|
588
|
+
def __eq__(self, other):
|
589
|
+
if not isinstance(other, Values):
|
590
|
+
return False
|
591
|
+
if self._type != other._type:
|
592
|
+
return False
|
593
|
+
if self._values is None:
|
594
|
+
return other._values is None
|
595
|
+
elif other._values is None:
|
596
|
+
return False
|
597
|
+
if len(self) != len(other):
|
598
|
+
return False
|
599
|
+
if self._isMap:
|
600
|
+
for key, value in self:
|
601
|
+
if key not in other:
|
602
|
+
return False
|
603
|
+
if value != other[key]:
|
604
|
+
return False
|
605
|
+
if self._isList:
|
606
|
+
for ix, value in enumerate(self):
|
607
|
+
if value != other[ix]:
|
608
|
+
return False
|
609
|
+
return True
|
610
|
+
|
611
|
+
def __str__(self):
|
612
|
+
return format(self)
|
613
|
+
|
614
|
+
def __format__(self, spec='yaml'):
|
615
|
+
return _formatObject(self, spec)
|
616
|
+
|
617
|
+
def _fullName(self, key=None):
|
618
|
+
if self._key is not None:
|
619
|
+
if self._parent is not None:
|
620
|
+
name = self._parent._fullName(self._key)
|
621
|
+
else:
|
622
|
+
name = str(self._key)
|
623
|
+
if key is not None:
|
624
|
+
if self._isMap:
|
625
|
+
if key.isidentifier():
|
626
|
+
name += f".{key}"
|
627
|
+
else:
|
628
|
+
name += f"['{key}']"
|
629
|
+
elif self._isList:
|
630
|
+
name += f"[{key}]"
|
631
|
+
else:
|
632
|
+
if isinstance(key, int):
|
633
|
+
name += f"[{key}]"
|
634
|
+
else:
|
635
|
+
if key.isidentifier():
|
636
|
+
name += f".{key}"
|
637
|
+
else:
|
638
|
+
name += f"['{key}']"
|
639
|
+
return name
|
640
|
+
if key is not None:
|
641
|
+
return str(key)
|
642
|
+
return ''
|
643
|
+
|
644
|
+
def _create_child(self, key, type):
|
645
|
+
if self._readOnly:
|
646
|
+
raise ValueError(f"{self._readOnly} is read only")
|
647
|
+
if isinstance(key, str):
|
648
|
+
if not self._isMap:
|
649
|
+
if not self._isUnknown:
|
650
|
+
raise ValueError('Invalid key, must be a str for maps')
|
651
|
+
self.__dict__['_type'] = self.Type.MAP
|
652
|
+
if self._values is None:
|
653
|
+
if self._parent is None:
|
654
|
+
self.__dict__['_values'] = google.protobuf.struct_pb2.Struct()
|
655
|
+
else:
|
656
|
+
self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
|
657
|
+
struct_value = self._values.fields[key]
|
658
|
+
elif isinstance(key, int):
|
659
|
+
if not self._isList:
|
660
|
+
if not self._isUnknown:
|
661
|
+
raise ValueError('Invalid key, must be an int for lists')
|
662
|
+
self.__dict__['_type'] = self.Type.LIST
|
663
|
+
if self._values is None:
|
664
|
+
if self._parent is None:
|
665
|
+
self.__dict__['_values'] = google.protobuf.struct_pb2.ListValue()
|
666
|
+
else:
|
667
|
+
self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
|
668
|
+
while key >= len(self._values.values):
|
669
|
+
self._values.values.add()
|
670
|
+
struct_value = self._values.values[key]
|
671
|
+
else:
|
672
|
+
raise ValueError('Unexpected key type')
|
673
|
+
if type == self.Type.MAP:
|
674
|
+
if not struct_value.HasField('struct_value'):
|
675
|
+
struct_value.struct_value.Clear()
|
676
|
+
return struct_value.struct_value
|
677
|
+
if type == self.Type.LIST:
|
678
|
+
if not struct_value.HasField('list_value'):
|
679
|
+
struct_value.list_value.Clear()
|
680
|
+
return struct_value.list_value
|
681
|
+
raise ValueError(f"Unexpected type: {type}")
|
682
|
+
|
683
|
+
def __call__(self, *args, **kwargs):
|
684
|
+
if self._readOnly:
|
685
|
+
raise ValueError(f"{self._readOnly} is read only")
|
686
|
+
self._cache.clear()
|
687
|
+
self._unknowns.clear()
|
688
|
+
if len(kwargs):
|
689
|
+
if not self._isMap:
|
690
|
+
if not self._isUnknown:
|
691
|
+
raise ValueError('Cannot specify kwargs on lists')
|
692
|
+
self.__dict__['_type'] = self.Type.MAP
|
693
|
+
if len(args):
|
694
|
+
raise ValueError('Connect specify args on maps')
|
695
|
+
if self._values is None:
|
696
|
+
if self._parent is None:
|
697
|
+
self.__dict__['_values'] = google.protobuf.struct_pb2.Struct()
|
698
|
+
else:
|
699
|
+
self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
|
700
|
+
self._values.Clear()
|
701
|
+
for key, value in kwargs.items():
|
702
|
+
self[key] = value
|
703
|
+
elif len(args):
|
704
|
+
if not self._isList:
|
705
|
+
if not self._isUnknown:
|
706
|
+
raise ValueError('Cannot specify args on maps')
|
707
|
+
self.__dict__['_type'] = self.Type.LIST
|
708
|
+
if len(kwargs):
|
709
|
+
raise ValueError('Connect specify kwargs on lists')
|
710
|
+
if self._values is None:
|
711
|
+
if self._parent is None:
|
712
|
+
self.__dict__['_values'] = google.protobuf.struct_pb2.ListValue()
|
713
|
+
else:
|
714
|
+
self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
|
715
|
+
self._values.Clear()
|
716
|
+
for key in range(len(args)):
|
717
|
+
self[key] = args[key]
|
718
|
+
return self
|
719
|
+
|
720
|
+
def __setattr__(self, key, value):
|
721
|
+
self[key] = value
|
722
|
+
|
723
|
+
def __setitem__(self, key, value):
|
724
|
+
if self._readOnly:
|
725
|
+
raise ValueError(f"{self._readOnly} is read only")
|
726
|
+
if isinstance(key, str):
|
727
|
+
if not self._isMap:
|
728
|
+
if not self._isUnknown:
|
729
|
+
raise ValueError('Invalid key, must be a str for maps')
|
730
|
+
self.__dict__['_type'] = self.Type.MAP
|
731
|
+
if self._values is None:
|
732
|
+
if self._parent is None:
|
733
|
+
self.__dict__['_values'] = google.protobuf.struct_pb2.Struct()
|
734
|
+
else:
|
735
|
+
self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
|
736
|
+
values = self._values.fields
|
737
|
+
elif isinstance(key, int):
|
738
|
+
if not self._isList:
|
739
|
+
if not self._isUnknown:
|
740
|
+
raise ValueError('Invalid key, must be an int for lists')
|
741
|
+
self.__dict__['_type'] = self.Type.LIST
|
742
|
+
if self._values is None:
|
743
|
+
if self._parent is None:
|
744
|
+
self.__dict__['_values'] = google.protobuf.struct_pb2.ListValue()
|
745
|
+
else:
|
746
|
+
self.__dict__['_values'] = self._parent._create_child(self._key, self._type)
|
747
|
+
values = self._values.values
|
748
|
+
if key == append:
|
749
|
+
key = len(values)
|
750
|
+
elif key < 0:
|
751
|
+
key = len(values) + key
|
752
|
+
while key >= len(values):
|
753
|
+
values.add()
|
754
|
+
else:
|
755
|
+
raise ValueError('Unexpected key type')
|
756
|
+
self._cache.pop(key, None)
|
757
|
+
self._unknowns.pop(key, None)
|
758
|
+
if isinstance(value, ProtobufValue):
|
759
|
+
value = value._protobuf_value
|
760
|
+
if value is None:
|
761
|
+
values[key].null_value = 0
|
762
|
+
elif isinstance(value, bool): # Must be before int check
|
763
|
+
values[key].bool_value = value
|
764
|
+
elif isinstance(value, str):
|
765
|
+
values[key].string_value = value
|
766
|
+
elif isinstance(value, (int, float)):
|
767
|
+
values[key].number_value = value
|
768
|
+
elif isinstance(value, dict):
|
769
|
+
values[key].struct_value.Clear()
|
770
|
+
self[key](**value)
|
771
|
+
elif isinstance(value, (list, tuple)):
|
772
|
+
values[key].list_value.Clear()
|
773
|
+
self[key](*value)
|
774
|
+
elif isinstance(value, Values):
|
775
|
+
if value._isMap:
|
776
|
+
values[key].struct_value.Clear()
|
777
|
+
self[key](**{k:v for k,v in value})
|
778
|
+
elif value._isList:
|
779
|
+
values[key].list_value.Clear()
|
780
|
+
self[key](*[v for v in value])
|
781
|
+
else:
|
782
|
+
self._unknowns[key] = value
|
783
|
+
if self._isMap:
|
784
|
+
if key in values:
|
785
|
+
del values[key]
|
786
|
+
elif self._isList:
|
787
|
+
if key < len(values):
|
788
|
+
values[key].Clear()
|
789
|
+
for ix in reversed(range(len(values))):
|
790
|
+
if ix not in self._unknowns:
|
791
|
+
break
|
792
|
+
del values[ix]
|
793
|
+
else:
|
794
|
+
raise ValueError(f"Unexpected type: {value.__class__}")
|
795
|
+
|
796
|
+
def __delattr__(self, key):
|
797
|
+
del self[key]
|
798
|
+
|
799
|
+
def __delitem__(self, key):
|
800
|
+
if self._readOnly:
|
801
|
+
raise ValueError(f"{self._readOnly} is read only")
|
802
|
+
if isinstance(key, str):
|
803
|
+
if not self._isMap:
|
804
|
+
if not self._isUnknown:
|
805
|
+
raise ValueError('Invalid key, must be a str for maps')
|
806
|
+
self.__dict__['_type'] = self.Type.MAP
|
807
|
+
if self._values is not None:
|
808
|
+
if key in self._values:
|
809
|
+
del self._values[key]
|
810
|
+
self._cache.pop(key, None)
|
811
|
+
self._unknowns.pop(key, None)
|
812
|
+
elif isinstance(key, int):
|
813
|
+
if not self._isList:
|
814
|
+
if not self._isUnknown:
|
815
|
+
raise ValueError('Invalid key, must be an int for lists')
|
816
|
+
self.__dict__['_type'] = self.Type.LIST
|
817
|
+
if self._values is not None:
|
818
|
+
if key < len(self._values):
|
819
|
+
del self._values[key]
|
820
|
+
self._cache.pop(key, None)
|
821
|
+
self._unknowns.pop(key, None)
|
822
|
+
for ix in sorted(self._unknowns.keys()):
|
823
|
+
if ix > key:
|
824
|
+
self._cache.pop(ix, None)
|
825
|
+
self._unknowns[ix - 1] = self._unknowns[ix]
|
826
|
+
del self._unknowns[ix]
|
827
|
+
for ix in reversed(range(len(self._values))):
|
828
|
+
if ix not in self._unknowns:
|
829
|
+
break
|
830
|
+
del self._values[ix]
|
831
|
+
else:
|
832
|
+
raise ValueError('Unexpected key type')
|
833
|
+
|
834
|
+
@property
|
835
|
+
def _isUnknown(self):
|
836
|
+
return self._type == self.Type.UNKNOWN
|
837
|
+
|
838
|
+
@property
|
839
|
+
def _isMap(self):
|
840
|
+
return self._type == self.Type.MAP
|
841
|
+
|
842
|
+
@property
|
843
|
+
def _isList(self):
|
844
|
+
return self._type == self.Type.LIST
|
845
|
+
|
846
|
+
@property
|
847
|
+
def _getUnknowns(self):
|
848
|
+
unknowns = {}
|
849
|
+
for key, unknown in self._unknowns.items():
|
850
|
+
unknowns[self._fullName(key)] = unknown._fullName()
|
851
|
+
if self._isMap:
|
852
|
+
for key, value in self:
|
853
|
+
if isinstance(value, Values):
|
854
|
+
unknowns.update(value._getUnknowns)
|
855
|
+
elif self._isList:
|
856
|
+
for value in self:
|
857
|
+
if isinstance(value, Values):
|
858
|
+
unknowns.update(value._getUnknowns)
|
859
|
+
return unknowns
|
860
|
+
|
861
|
+
def _patchUnknowns(self, patches):
|
862
|
+
for key in [key for key in self._unknowns.keys()]:
|
863
|
+
self[key] = patches[key]
|
864
|
+
if self._isMap:
|
865
|
+
for key, value in self:
|
866
|
+
if isinstance(value, Values) and len(value):
|
867
|
+
patch = patches[key]
|
868
|
+
if isinstance(patch, Values) and patch._type == value._type and len(patch):
|
869
|
+
value._patchUnknowns(patch)
|
870
|
+
elif self._isList:
|
871
|
+
for ix, value in enumerate(self):
|
872
|
+
if isinstance(value, Values) and len(value):
|
873
|
+
patch = patches[ix]
|
874
|
+
if isinstance(patch, Values) and patch._type == value._type and len(patch):
|
875
|
+
value._patchUnknowns(patch)
|
876
|
+
|
877
|
+
|
878
|
+
def _formatObject(object, spec):
|
879
|
+
if spec == 'json':
|
880
|
+
return json.dumps(object, indent=2, cls=_JSONEncoder)
|
881
|
+
if spec == 'jsonc':
|
882
|
+
return json.dumps(object, separators=(',', ':'), cls=_JSONEncoder)
|
883
|
+
if spec == 'protobuf':
|
884
|
+
if isinstance(object, Message):
|
885
|
+
return str(object._message)
|
886
|
+
if isinstance(object, (MapMessage, RepeatedMessage)):
|
887
|
+
return str(object._messages)
|
888
|
+
if isinstance(object, Values):
|
889
|
+
return str(object._values)
|
890
|
+
return format(object)
|
891
|
+
return yaml.dump(object, Dumper=_Dumper)
|
892
|
+
|
893
|
+
|
894
|
+
class _JSONEncoder(json.JSONEncoder):
|
895
|
+
def default(self, object):
|
896
|
+
if isinstance(object, (Message, MapMessage)):
|
897
|
+
if object:
|
898
|
+
return {key: value for key, value in object}
|
899
|
+
return None
|
900
|
+
if isinstance(object, RepeatedMessage):
|
901
|
+
if object:
|
902
|
+
return [value for value in object]
|
903
|
+
return None
|
904
|
+
if isinstance(object, Values):
|
905
|
+
if object._isMap:
|
906
|
+
return {key: value for key, value in object}
|
907
|
+
if object._isList:
|
908
|
+
return [value for value in object]
|
909
|
+
if object._isUnknown:
|
910
|
+
return '<<UNKNOWN>>'
|
911
|
+
return '<<UNEXPECTED>>'
|
912
|
+
if isinstance(object, datetime.datetime):
|
913
|
+
return object.isoformat()
|
914
|
+
return super(JSONEncoder, self).default(object)
|
915
|
+
|
916
|
+
|
917
|
+
class _Dumper(yaml.SafeDumper):
|
918
|
+
|
919
|
+
def represent_str(self, data):
|
920
|
+
return self.represent_scalar('tag:yaml.org,2002:str', data, '|' if '\n' in data else None)
|
921
|
+
|
922
|
+
def represent_message_dict(self, message):
|
923
|
+
return self.represent_dict({key: value for key, value in message})
|
924
|
+
|
925
|
+
def represent_message_list(self, messages):
|
926
|
+
return self.represent_list([value for value in messages])
|
927
|
+
|
928
|
+
def represent_values(self, values):
|
929
|
+
if values._isMap:
|
930
|
+
return self.represent_dict({key: value for key, value in values})
|
931
|
+
if values._isList:
|
932
|
+
return self.represent_list([value for value in values])
|
933
|
+
if values._isUnknown:
|
934
|
+
return self.represent_scalar('tag:yaml.org,2002:str', '<<UNKNOWN>>')
|
935
|
+
return self.represent_scalar('tag:yaml.org,2002:str', '<<UNEXPECTED>>')
|
936
|
+
|
937
|
+
_Dumper.add_representer(str, _Dumper.represent_str)
|
938
|
+
_Dumper.add_representer(Message, _Dumper.represent_message_dict)
|
939
|
+
_Dumper.add_representer(MapMessage, _Dumper.represent_message_dict)
|
940
|
+
_Dumper.add_representer(RepeatedMessage, _Dumper.represent_message_list)
|
941
|
+
_Dumper.add_representer(Values, _Dumper.represent_values)
|