crossplane-function-pythonic 0.6.3__py3-none-any.whl → 0.7.0__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.
@@ -19,27 +19,37 @@ import base64
19
19
  import datetime
20
20
  import json
21
21
  import sys
22
+ import typing
22
23
 
24
+ import google.protobuf.descriptor
25
+ import google.protobuf.message
23
26
  import google.protobuf.struct_pb2
24
27
  import yaml
25
-
26
- _Unknown = object()
28
+ from google.protobuf.internal.containers import (
29
+ MessageMap,
30
+ RepeatedCompositeFieldContainer,
31
+ RepeatedScalarFieldContainer,
32
+ ScalarMap,
33
+ )
34
+
35
+ type TypeString = str | Value | FieldMessage
36
+ _Unknown = sentinel("_Unknown")
27
37
  append = sys.maxsize
28
38
 
29
39
 
30
- def Map(**kwargs):
40
+ def Map(**kwargs) -> Value:
31
41
  return Value(None, None, kwargs)
32
42
 
33
43
 
34
- def List(*args):
44
+ def List(*args) -> Value:
35
45
  return Value(None, None, args)
36
46
 
37
47
 
38
- def Unknown():
48
+ def Unknown() -> Value:
39
49
  return Value(None, None)
40
50
 
41
51
 
42
- def Yaml(string, readOnly=None):
52
+ def Yaml(string: TypeString, readOnly: str | bool = False) -> Value | FieldMessage:
43
53
  if isinstance(string, (FieldMessage, Value)):
44
54
  if not string:
45
55
  return string
@@ -47,7 +57,7 @@ def Yaml(string, readOnly=None):
47
57
  return Value(None, None, yaml.safe_load(string), readOnly)
48
58
 
49
59
 
50
- def YamlAll(string, readOnly=None):
60
+ def YamlAll(string: TypeString, readOnly: str | bool = False) -> Value | FieldMessage:
51
61
  if isinstance(string, (FieldMessage, Value)):
52
62
  if not string:
53
63
  return string
@@ -57,7 +67,7 @@ def YamlAll(string, readOnly=None):
57
67
  )
58
68
 
59
69
 
60
- def Json(string, readOnly=None):
70
+ def Json(string: TypeString, readOnly: str | bool = False) -> Value | FieldMessage:
61
71
  if isinstance(string, (FieldMessage, Value)):
62
72
  if not string:
63
73
  return string
@@ -65,7 +75,7 @@ def Json(string, readOnly=None):
65
75
  return Value(None, None, json.loads(string), readOnly)
66
76
 
67
77
 
68
- def B64Encode(string):
78
+ def B64Encode(string: TypeString) -> TypeString:
69
79
  if isinstance(string, (FieldMessage, Value)):
70
80
  if not string:
71
81
  return string
@@ -73,7 +83,7 @@ def B64Encode(string):
73
83
  return base64.b64encode(string.encode("utf-8")).decode("utf-8")
74
84
 
75
85
 
76
- def B64Decode(string):
86
+ def B64Decode(string: TypeString) -> TypeString:
77
87
  if isinstance(string, (FieldMessage, Value)):
78
88
  if not string:
79
89
  return string
@@ -81,27 +91,84 @@ def B64Decode(string):
81
91
  return base64.b64decode(string.encode("utf-8")).decode("utf-8")
82
92
 
83
93
 
84
- class Message:
85
- def __init__(self, parent, key, descriptor, message=_Unknown, readOnly=False):
86
- self._set_attribute("_parent", parent)
87
- self._set_attribute("_key", key)
88
- self._set_attribute("_descriptor", descriptor)
89
- self._set_attribute("_message", message)
90
- self._set_attribute("_readOnly", readOnly)
91
- self._set_attribute("_cache", {})
94
+ class TypeProtobuf:
95
+ def __getattr__(self, key):
96
+ if key not in self.__dict__:
97
+ raise AttributeError(
98
+ f"{self.__class__.__name__} attribute does not exist: {key}"
99
+ )
100
+ return self.__dict__[key]
101
+
102
+ def __getitem__(self, key):
103
+ raise ValueError(f"{self.__class__.__name__} does not have dynamic items")
104
+
105
+ def __iter__(self):
106
+ raise ValueError(f"{self.__class__.__name__} is not iterable")
92
107
 
93
- def _set_attribute(self, key, value):
108
+ def __int__(self):
109
+ raise ValueError(f"{self.__class__.__name__} is not an integer")
110
+
111
+ def __float__(self):
112
+ raise ValueError(f"{self.__class__.__name__} is not a float")
113
+
114
+ def __call__(self, *args, **kwargs):
115
+ raise ValueError(f"{self.__class__.__name__} is not callable")
116
+
117
+ def __setattr__(self, key, value):
94
118
  self.__dict__[key] = value
95
119
 
96
- def __getattr__(self, key):
120
+ def __setitem__(self, key, value):
121
+ raise ValueError(f"{self.__class__.__name__} does not have dynamic items")
122
+
123
+ def __delattr__(self, key):
124
+ raise ValueError(f"{self.__class__.__name__} does not have dynamic attributes")
125
+
126
+ def __delitem__(self, key):
127
+ raise ValueError(f"{self.__class__.__name__} does not have dynamic items")
128
+
129
+ def append(self, value):
130
+ raise ValueError(f"{self.__class__.__name__} does not have dynamic items")
131
+
132
+
133
+ class Message(TypeProtobuf):
134
+ def __init__(
135
+ self,
136
+ parent: Message | MapMessage | RepeatedMessage | None,
137
+ key: str | int | Value | FieldMessage | None,
138
+ descriptor: google.protobuf.descriptor.Descriptor,
139
+ message: google.protobuf.message.Message | _Unknown = _Unknown,
140
+ readOnly: str | bool = False,
141
+ ):
142
+ self._parent: Message | MapMessage | RepeatedMessage | None = parent
143
+ self._key = key
144
+ self._descriptor: google.protobuf.descriptor.Descriptor = descriptor
145
+ self._message: google.protobuf.message.Message | _Unknown = message
146
+ self._readOnly: str | bool = readOnly
147
+ self._cache: dict[str, typing.Any] = {}
148
+
149
+ def _ensure_message(self):
150
+ if self._message is _Unknown:
151
+ if self._parent is None:
152
+ raise ValueError(
153
+ "Messages does not have parent to create internal message"
154
+ )
155
+ self._message: google.protobuf.message.Message = self._parent._create_child(
156
+ self._key
157
+ )
158
+
159
+ def __getattr__(
160
+ self, key
161
+ ) -> Message | MapMessage | RepeatedMessage | FieldMessage | Value:
97
162
  return self[key]
98
163
 
99
- def __getitem__(self, key):
164
+ def __getitem__(
165
+ self, key
166
+ ) -> Message | MapMessage | RepeatedMessage | FieldMessage | Value:
100
167
  key = self._validate_key(key)
101
168
  if key in self._cache:
102
169
  return self._cache[key]
103
170
  field = self._descriptor.fields_by_name.get(key)
104
- if not field:
171
+ if field is None:
105
172
  raise AttributeError(obj=self, name=key)
106
173
  if self._message is _Unknown:
107
174
  value = _Unknown
@@ -112,6 +179,7 @@ class Message:
112
179
  if field.is_repeated:
113
180
  if (
114
181
  field.type == field.TYPE_MESSAGE
182
+ and field.message_type
115
183
  and field.message_type.GetOptions().map_entry
116
184
  ):
117
185
  value = MapMessage(
@@ -123,7 +191,7 @@ class Message:
123
191
  )
124
192
  else:
125
193
  value = RepeatedMessage(self, key, field, value, self._readOnly)
126
- elif field.type == field.TYPE_MESSAGE:
194
+ elif field.type == field.TYPE_MESSAGE and field.message_type:
127
195
  if field.message_type.name in ("Struct", "ListValue"):
128
196
  value = Value(self, key, value, self._readOnly)
129
197
  else:
@@ -199,23 +267,24 @@ class Message:
199
267
  if self._readOnly:
200
268
  raise ValueError(f"{self._readOnly} is read only")
201
269
  key = self._validate_key(key)
202
- if self._message is _Unknown:
203
- self.__dict__["_message"] = self._parent._create_child(self._key)
270
+ self._ensure_message()
204
271
  return getattr(self._message, key)
205
272
 
206
273
  def __call__(self, **kwargs):
207
274
  if self._readOnly:
208
275
  raise ValueError(f"{self._readOnly} is read only")
209
- if self._message is _Unknown:
210
- self.__dict__["_message"] = self._parent._create_child(self._key)
211
- self._message.Clear()
276
+ self._ensure_message()
277
+ typing.cast(google.protobuf.message.Message, self._message).Clear()
212
278
  self._cache.clear()
213
279
  for key, value in kwargs.items():
214
280
  self[key] = value
215
281
  return self
216
282
 
217
283
  def __setattr__(self, key, value):
218
- self[key] = value
284
+ if key in ("_parent", "_key", "_descriptor", "_message", "_readOnly", "_cache"):
285
+ self.__dict__[key] = value
286
+ else:
287
+ self[key] = value
219
288
 
220
289
  def __setitem__(self, key, value):
221
290
  if self._readOnly:
@@ -223,19 +292,34 @@ class Message:
223
292
  key = self._validate_key(key)
224
293
  if key not in self._descriptor.fields_by_name:
225
294
  raise AttributeError(obj=self, name=key)
226
- field = self._descriptor.fields_by_name[key]
227
- if self._message is _Unknown:
228
- self.__dict__["_message"] = self._parent._create_child(self._key)
229
- if isinstance(value, Message):
230
- value = value._message
231
- elif isinstance(value, (MapMessage, RepeatedMessage)):
232
- value = value._messages
233
- elif isinstance(value, FieldMessage):
295
+ if isinstance(value, (Message, MapMessage)):
296
+ for k, v in value:
297
+ self[key][k] = v
298
+ return
299
+ if isinstance(value, dict):
300
+ for k, v in value.items():
301
+ self[key][k] = v
302
+ return
303
+ if isinstance(value, (RepeatedMessage, list, tuple)):
304
+ for ix, v in enumerate(value):
305
+ self[key][ix] = v
306
+ return
307
+ if isinstance(value, FieldMessage):
234
308
  value = value._value
235
309
  elif isinstance(value, Value):
310
+ if value._isMap:
311
+ for k, v in value:
312
+ self[key][k] = v
313
+ return
314
+ if value._isList:
315
+ for ix, v in enumerate(value):
316
+ self[key][ix] = v
317
+ return
236
318
  value = value._raw
319
+ field = self._descriptor.fields_by_name[key]
237
320
  if field.type == field.TYPE_BYTES and isinstance(value, str):
238
321
  value = value.encode("utf-8")
322
+ self._ensure_message()
239
323
  setattr(self._message, key, value)
240
324
  self._cache.pop(key, None)
241
325
 
@@ -262,17 +346,21 @@ class Message:
262
346
  return key
263
347
 
264
348
 
265
- class MapMessage:
266
- def __init__(self, parent, key, field, messages=_Unknown, readOnly=False):
267
- self._set_attribute("_parent", parent)
268
- self._set_attribute("_key", key)
269
- self._set_attribute("_field", field)
270
- self._set_attribute("_messages", messages)
271
- self._set_attribute("_readOnly", readOnly)
272
- self._set_attribute("_cache", {})
273
-
274
- def _set_attribute(self, key, value):
275
- self.__dict__[key] = value
349
+ class MapMessage(TypeProtobuf):
350
+ def __init__(
351
+ self,
352
+ parent: Message,
353
+ key: str,
354
+ field: google.protobuf.descriptor.FieldDescriptor,
355
+ messages: ScalarMap | MessageMap | _Unknown = _Unknown,
356
+ readOnly: str | bool = False,
357
+ ):
358
+ self._parent: Message = parent
359
+ self._key: str = key
360
+ self._field: google.protobuf.descriptor.FieldDescriptor = field
361
+ self._messages: ScalarMap | MessageMap | _Unknown = messages
362
+ self._readOnly = readOnly
363
+ self._cache: dict[str, typing.Any] = {}
276
364
 
277
365
  def __getattr__(self, key):
278
366
  return self[key]
@@ -287,8 +375,8 @@ class MapMessage:
287
375
  value = self._messages[key]
288
376
  if value is None and self._field.has_default_value:
289
377
  value = self._field.default_value
290
- if self._field.type == self._field.TYPE_MESSAGE:
291
- if self._field.message_type.name in ("Struct", "ListValue"):
378
+ if self._field.type == self._field.TYPE_MESSAGE and self._field.message_type:
379
+ if str(self._field.message_type.name) in ("Struct", "ListValue"):
292
380
  value = Value(self, key, value, self._readOnly)
293
381
  else:
294
382
  value = Message(
@@ -306,7 +394,9 @@ class MapMessage:
306
394
  return 0 if self._messages is _Unknown else len(self._messages)
307
395
 
308
396
  def __contains__(self, key):
309
- return self._messages is not _Unknown and key in self._messages
397
+ return (
398
+ self._messages is not _Unknown and self._validate_key(key) in self._messages
399
+ )
310
400
 
311
401
  def __iter__(self):
312
402
  if self._messages is not _Unknown:
@@ -314,7 +404,7 @@ class MapMessage:
314
404
  yield key, self[key]
315
405
 
316
406
  def __hash__(self):
317
- if self._nessages is not None:
407
+ if self._messages is not _Unknown:
318
408
  return hash(
319
409
  tuple(
320
410
  hash(item) for item in sorted(iter(self), key=lambda item: item[0])
@@ -329,6 +419,7 @@ class MapMessage:
329
419
  return False
330
420
  if (
331
421
  self._field.type == self._field.TYPE_MESSAGE
422
+ and self._field.message_type
332
423
  and self._field.message_type.full_name
333
424
  != other._field.message_type.full_name
334
425
  ):
@@ -353,63 +444,63 @@ class MapMessage:
353
444
  return _formatObject(self, spec)
354
445
 
355
446
  def _fullName(self, key=None):
356
- if self._key is not None:
357
- if self._parent is not None:
358
- name = self._parent._fullName(self._key)
359
- else:
360
- name = str(self._key)
361
- if key is not None:
362
- if "." in key:
363
- name += f"[{key}]"
364
- else:
365
- name += f".{key}"
366
- return name
447
+ name = self._parent._fullName(self._key)
367
448
  if key is not None:
368
- return str(key)
369
- return ""
449
+ if "." in key:
450
+ name += f"[{key}]"
451
+ else:
452
+ name += f".{key}"
453
+ return name
370
454
 
371
455
  def _create_child(self, key):
372
456
  if self._readOnly:
373
457
  raise ValueError(f"{self._readOnly} is read only")
374
458
  key = self._validate_key(key)
375
459
  if self._messages is _Unknown:
376
- self.__dict__["_messages"] = self._parent._create_child(self._key)
460
+ self._messages = self._parent._create_child(self._key)
377
461
  return self._messages[key]
378
462
 
379
463
  def __call__(self, **kwargs):
380
464
  if self._readOnly:
381
465
  raise ValueError(f"{self._readOnly} is read only")
382
466
  if self._messages is _Unknown:
383
- self.__dict__["_messages"] = self._parent._create_child(self._key)
467
+ self._messages: ScalarMap | MessageMap = self._parent._create_child(
468
+ self._key
469
+ )
384
470
  self._messages.clear()
385
471
  self._cache.clear()
386
472
  for key, value in kwargs.items():
387
473
  self[key] = value
388
474
  return self
389
475
 
390
- def __setattr__(self, key, message):
391
- self[key] = message
476
+ def __setattr__(self, key, value):
477
+ if key in ("_parent", "_key", "_field", "_messages", "_readOnly", "_cache"):
478
+ self.__dict__[key] = value
479
+ else:
480
+ self[key] = value
392
481
 
393
- def __setitem__(self, key, message):
482
+ def __setitem__(self, key, value):
394
483
  if self._readOnly:
395
484
  raise ValueError(f"{self._readOnly} is read only")
396
485
  key = self._validate_key(key)
397
486
  if self._messages is _Unknown:
398
- self._messages = self._parent._create_child(self._key)
399
- if isinstance(message, Message):
400
- message = message._message
401
- elif isinstance(message, (MapMessage, RepeatedMessage)):
402
- message = message._messages
403
- elif isinstance(message, FieldMessage):
404
- message = message._value
405
- elif isinstance(message, Value):
406
- message = message._raw
407
- if message is _Unknown:
487
+ self._messages: ScalarMap | MessageMap = self._parent._create_child(
488
+ self._key
489
+ )
490
+ if isinstance(value, Message):
491
+ value = value._message
492
+ elif isinstance(value, (MapMessage, RepeatedMessage)):
493
+ value = value._messages
494
+ elif isinstance(value, FieldMessage):
495
+ value = value._value
496
+ elif isinstance(value, Value):
497
+ value = value._raw
498
+ if value is _Unknown:
408
499
  self._messages.pop(key, None)
409
500
  else:
410
- if self._field.type == self._field.TYPE_BYTES and isinstance(message, str):
411
- message = message.encode("utf-8")
412
- self._messages[key] = message
501
+ if self._field.type == self._field.TYPE_BYTES and isinstance(value, str):
502
+ value = value.encode("utf-8")
503
+ self._messages[key] = value
413
504
  self._cache.pop(key, None)
414
505
 
415
506
  def __delattr__(self, key):
@@ -434,14 +525,25 @@ class MapMessage:
434
525
  return key
435
526
 
436
527
 
437
- class RepeatedMessage:
438
- def __init__(self, parent, key, field, messages=_Unknown, readOnly=False):
439
- self._parent = parent
440
- self._key = key
441
- self._field = field
442
- self._messages = messages
443
- self._readOnly = readOnly
444
- self._cache = {}
528
+ class RepeatedMessage(TypeProtobuf):
529
+ def __init__(
530
+ self,
531
+ parent: Message,
532
+ key: str,
533
+ field: google.protobuf.descriptor.FieldDescriptor,
534
+ messages: RepeatedCompositeFieldContainer
535
+ | RepeatedScalarFieldContainer
536
+ | _Unknown = _Unknown,
537
+ readOnly: str | bool = False,
538
+ ):
539
+ self._parent: Message = parent
540
+ self._key: str = key
541
+ self._field: google.protobuf.descriptor.FieldDescriptor = field
542
+ self._messages: (
543
+ RepeatedCompositeFieldContainer | RepeatedScalarFieldContainer | _Unknown
544
+ ) = messages
545
+ self._readOnly: str | bool = readOnly
546
+ self._cache: dict[int, typing.Any] = {}
445
547
 
446
548
  def __getitem__(self, key):
447
549
  key = self._validate_key(key)
@@ -466,7 +568,7 @@ class RepeatedMessage:
466
568
  value = self._messages[key]
467
569
  if value is None and self._field.has_default_value:
468
570
  value = self._field.default_value
469
- if self._field.type == self._field.TYPE_MESSAGE:
571
+ if self._field.type == self._field.TYPE_MESSAGE and self._field.message_type:
470
572
  if self._field.message_type.name in ("Struct", "ListValue"):
471
573
  value = Value(self, key, value, self._readOnly)
472
574
  else:
@@ -508,6 +610,7 @@ class RepeatedMessage:
508
610
  return False
509
611
  if (
510
612
  self._field.type == self._field.TYPE_MESSAGE
613
+ and self._field.message_type
511
614
  and self._field.message_type.full_name
512
615
  != other._field.message_type.full_name
513
616
  ):
@@ -547,27 +650,31 @@ class RepeatedMessage:
547
650
  raise ValueError(f"{self._readOnly} is read only")
548
651
  key = self._validate_key(key)
549
652
  if self._messages is _Unknown:
550
- self.__dict__["_messages"] = self._parent._create_child(self._key)
653
+ self._messages: (
654
+ RepeatedCompositeFieldContainer | RepeatedScalarFieldContainer
655
+ ) = self._parent._create_child(self._key)
551
656
  if key == append:
552
657
  key = len(self._messages)
553
658
  elif key < 0:
554
659
  key = len(self._messages) + key
555
660
  while key >= len(self._messages):
556
- self._messages.add()
661
+ typing.cast(RepeatedCompositeFieldContainer, self._messages).add()
557
662
  return self._messages[key]
558
663
 
559
664
  def __call__(self, *args):
560
665
  if self._readOnly:
561
666
  raise ValueError(f"{self._readOnly} is read only")
562
667
  if self._messages is _Unknown:
563
- self.__dict__["_messages"] = self._parent._create_child(self._key)
668
+ self._messages: (
669
+ RepeatedCompositeFieldContainer | RepeatedScalarFieldContainer
670
+ ) = self._parent._create_child(self._key)
564
671
  self._messages.clear()
565
672
  self._cache.clear()
566
673
  for arg in args:
567
674
  self.append(arg)
568
675
  return self
569
676
 
570
- def __setitem__(self, key, message):
677
+ def __setitem__(self, key, value):
571
678
  if self._readOnly:
572
679
  raise ValueError(f"{self._readOnly} is read only")
573
680
  key = self._validate_key(key)
@@ -575,25 +682,25 @@ class RepeatedMessage:
575
682
  self._messages = self._parent._create_child(self._key)
576
683
  if key < 0:
577
684
  key = len(self._messages) + key
578
- if isinstance(message, Message):
579
- message = message._message
580
- elif isinstance(message, (MapMessage, RepeatedMessage)):
581
- message = message._messages
582
- elif isinstance(message, FieldMessage):
583
- message = message._value
584
- elif isinstance(message, Value):
585
- message = message._raw
586
- if message is _Unknown:
685
+ if isinstance(value, Message):
686
+ value = value._message
687
+ elif isinstance(value, (MapMessage, RepeatedMessage)):
688
+ value = value._messages
689
+ elif isinstance(value, FieldMessage):
690
+ value = value._value
691
+ elif isinstance(value, Value):
692
+ value = value._raw
693
+ if value is _Unknown:
587
694
  if key < len(self._messages):
588
695
  self._messages.pop(key)
589
696
  self._cache.clear()
590
697
  else:
591
- if self._field.type == self._field.TYPE_BYTES and isinstance(message, str):
592
- message = message.encode("utf-8")
698
+ if self._field.type == self._field.TYPE_BYTES and isinstance(value, str):
699
+ value = value.encode("utf-8")
593
700
  if key >= len(self._messages):
594
- self._messages.append(message)
701
+ self._messages.append(value)
595
702
  else:
596
- self._messages[key] = message
703
+ self._messages[key] = value
597
704
  self._cache.pop(key, None)
598
705
 
599
706
  def __delitem__(self, key):
@@ -604,15 +711,27 @@ class RepeatedMessage:
604
711
  del self._messages[key]
605
712
  self._cache.pop(key, None)
606
713
 
607
- def append(self, message=_Unknown):
714
+ def append(self, value=_Unknown) -> TypeProtobuf:
608
715
  if self._readOnly:
609
716
  raise ValueError(f"{self._readOnly} is read only")
610
717
  if self._messages is _Unknown:
611
- self._messages = self._parent._create_child(self._key)
612
- if message is _Unknown:
613
- message = self._messages.add()
718
+ self._messages: RepeatedCompositeFieldContainer = (
719
+ self._parent._create_child(self._key)
720
+ )
721
+ if isinstance(value, Message):
722
+ value = value._message
723
+ elif isinstance(value, (MapMessage, RepeatedMessage)):
724
+ value = value._messages
725
+ elif isinstance(value, FieldMessage):
726
+ value = value._value
727
+ elif isinstance(value, Value):
728
+ value = value._raw
729
+ if value is _Unknown:
730
+ typing.cast(RepeatedCompositeFieldContainer, self._messages).add()
614
731
  else:
615
- message = self._messages.append(message)
732
+ if self._field.type == self._field.TYPE_BYTES and isinstance(value, str):
733
+ value = value.encode("utf-8")
734
+ self._messages.append(value)
616
735
  return self[len(self._messages) - 1]
617
736
 
618
737
  def _validate_key(self, key):
@@ -625,23 +744,29 @@ class RepeatedMessage:
625
744
  return key
626
745
 
627
746
 
628
- class FieldMessage:
629
- def __init__(self, parent, key, kind, value):
630
- self._parent = parent
631
- self._key = key
632
- self._kind = kind
633
- self._value = value
747
+ class FieldMessage(TypeProtobuf):
748
+ def __init__(
749
+ self,
750
+ parent: Message | MapMessage | RepeatedMessage,
751
+ key: str | int,
752
+ kind: int,
753
+ value: typing.Any,
754
+ ):
755
+ self._parent: Message | MapMessage | RepeatedMessage = parent
756
+ self._key: str | int = key
757
+ self._kind: int = kind
758
+ self._value: typing.Any = value
634
759
 
635
760
  def __bool__(self):
636
761
  return self._value is not _Unknown
637
762
 
638
763
  def __len__(self):
639
- if self._value is _Unknown:
764
+ if self._value is _Unknown or not isinstance(self._value, (bytes, str)):
640
765
  return 0
641
766
  return len(self._value)
642
767
 
643
768
  def __contains__(self, key):
644
- if self._value is _Unknown:
769
+ if self._value is _Unknown or not isinstance(self._value, (bytes, str)):
645
770
  return False
646
771
  return key in self._value
647
772
 
@@ -654,11 +779,16 @@ class FieldMessage:
654
779
  if self._value is _Unknown:
655
780
  return False
656
781
  if isinstance(other, FieldMessage):
657
- return self._value == other._value
782
+ other = other._value
783
+ if isinstance(self._value, str):
784
+ if isinstance(other, bytes):
785
+ other = other.decode("utf-8")
786
+ elif isinstance(self._value, bytes) and isinstance(other, str):
787
+ other = other.encode("utf-8")
658
788
  return self._value == other
659
789
 
660
790
  def __bytes__(self):
661
- if self._value is _Unknown:
791
+ if self._value is _Unknown or not isinstance(self._value, (bytes, str)):
662
792
  return None # noqa: PLE0308
663
793
  if isinstance(self._value, str):
664
794
  return self._value.encode("utf-8")
@@ -687,20 +817,13 @@ class FieldMessage:
687
817
  return float(self._value)
688
818
 
689
819
  def _fullName(self, key=None):
690
- if self._key is not None:
691
- if self._parent is not None:
692
- name = self._parent._fullName(self._key)
693
- else:
694
- name = str(self._key)
695
- if key is not None:
696
- if "." in key:
697
- name += f"[{key}]"
698
- else:
699
- name += f".{key}"
700
- return name
820
+ name = self._parent._fullName(self._key)
701
821
  if key is not None:
702
- return str(key)
703
- return ""
822
+ if "." in key:
823
+ name += f"[{key}]"
824
+ else:
825
+ name += f".{key}"
826
+ return name
704
827
 
705
828
 
706
829
  class ProtobufValue:
@@ -709,14 +832,20 @@ class ProtobufValue:
709
832
  return None
710
833
 
711
834
 
712
- class Value:
713
- def __init__(self, parent, key, value=_Unknown, readOnly=None):
714
- self._set_attribute("_parent", parent)
715
- self._set_attribute("_key", key)
716
- self._set_attribute("_dependencies", {})
717
- self._set_attribute("_unknowns", {})
718
- self._set_attribute("_cache", {})
719
- self._set_attribute("_readOnly", None)
835
+ class Value(TypeProtobuf):
836
+ def __init__(
837
+ self,
838
+ parent: Message | MapMessage | RepeatedMessage | Value | None,
839
+ key: str | int | None,
840
+ value: typing.Any | _Unknown = _Unknown,
841
+ readOnly: str | bool = False,
842
+ ):
843
+ self._parent: Message | MapMessage | RepeatedMessage | Value | None = parent
844
+ self._key: str | int | None = key
845
+ self._dependencies: dict[str | int, FieldMessage | Value] = {}
846
+ self._unknowns: dict[str | int, FieldMessage | Value] = {}
847
+ self._cache: dict[str | int, Value] = {}
848
+ self._readOnly: str | bool = False
720
849
  if (
721
850
  isinstance(
722
851
  value,
@@ -728,11 +857,13 @@ class Value:
728
857
  )
729
858
  or value is _Unknown
730
859
  ):
731
- self._set_attribute("_value", value)
860
+ self._value: typing.Any = value
732
861
  else:
733
- self._set_attribute("_value", google.protobuf.struct_pb2.Value())
862
+ self._value: google.protobuf.struct_pb2.Value = (
863
+ google.protobuf.struct_pb2.Value()
864
+ )
734
865
  if value is None:
735
- self._value.null_value = 0
866
+ self._value.null_value = google.protobuf.struct_pb2.NULL_VALUE
736
867
  elif isinstance(value, dict):
737
868
  self._value.struct_value.Clear()
738
869
  for k, v in value.items():
@@ -749,10 +880,7 @@ class Value:
749
880
  self._value.string_value = value
750
881
  else:
751
882
  raise ValueError(f"Unexpected Value type: {value.__class__}")
752
- self._set_attribute("_readOnly", readOnly)
753
-
754
- def _set_attribute(self, key, value):
755
- self.__dict__[key] = value
883
+ self._setReadOnly(readOnly)
756
884
 
757
885
  def __enter__(self):
758
886
  return self
@@ -760,12 +888,6 @@ class Value:
760
888
  def __exit__(self, exc_type, exc_value, traceback):
761
889
  pass
762
890
 
763
- def __aenter__(self):
764
- return self
765
-
766
- def __aexit__(self, exc_type, exc_value, traceback):
767
- pass
768
-
769
891
  def __getattr__(self, key):
770
892
  return self[key]
771
893
 
@@ -865,13 +987,13 @@ class Value:
865
987
  for ix in range(len(self._value.list_value.values)):
866
988
  yield self[ix]
867
989
  for ix in sorted(self._unknowns.keys()):
868
- if ix >= len(self._value.list_value.values):
990
+ if typing.cast(int, ix) >= len(self._value.list_value.values):
869
991
  yield self[ix]
870
992
  case "ListValue":
871
993
  for ix in range(len(self._value.values)):
872
994
  yield self[ix]
873
995
  for ix in sorted(self._unknowns.keys()):
874
- if ix >= len(self._value.values):
996
+ if typing.cast(int, ix) >= len(self._value.values):
875
997
  yield self[ix]
876
998
 
877
999
  def __hash__(self):
@@ -1068,7 +1190,18 @@ class Value:
1068
1190
  return self
1069
1191
 
1070
1192
  def __setattr__(self, key, value):
1071
- self[key] = value
1193
+ if key in (
1194
+ "_parent",
1195
+ "_key",
1196
+ "_value",
1197
+ "_dependencies",
1198
+ "_unknowns",
1199
+ "_cache",
1200
+ "_readOnly",
1201
+ ):
1202
+ self.__dict__[key] = value
1203
+ else:
1204
+ self[key] = value
1072
1205
 
1073
1206
  def __setitem__(self, key, value):
1074
1207
  if self._readOnly:
@@ -1102,7 +1235,7 @@ class Value:
1102
1235
  elif isinstance(value, bool): # Must be before int check
1103
1236
  values[key].bool_value = value
1104
1237
  elif isinstance(value, bytes):
1105
- values[key].string_value = value._value.decode("utf-8")
1238
+ values[key].string_value = value.decode("utf-8")
1106
1239
  elif isinstance(value, str):
1107
1240
  values[key].string_value = value
1108
1241
  elif isinstance(value, (int, float)):
@@ -1123,10 +1256,10 @@ class Value:
1123
1256
  values[key].string_value = value._value.decode("utf-8")
1124
1257
  elif value._value is None:
1125
1258
  values[key].null_value = value._value.null_value
1259
+ elif isinstance(value._value, bool): # Must be before number check
1260
+ values[key].bool_value = value._value
1126
1261
  elif isinstance(value._value, (int, float)):
1127
1262
  values[key].number_value = value._value
1128
- elif isinstance(value._value, bool):
1129
- values[key].bool_value = value._value
1130
1263
  elif value._value is _Unknown:
1131
1264
  self._setUnknown(key, value)
1132
1265
  else:
@@ -1169,6 +1302,8 @@ class Value:
1169
1302
  case "null_value":
1170
1303
  return self._value.null_value
1171
1304
  case "number_value":
1305
+ if self._value.number_value.is_integer():
1306
+ return int(self._value.number_value)
1172
1307
  return self._value.number_value
1173
1308
  case "bool_value":
1174
1309
  return self._value.bool_value
@@ -1241,12 +1376,14 @@ class Value:
1241
1376
  for ix in sorted(self._dependencies.keys()):
1242
1377
  if ix > key:
1243
1378
  self._cache.pop(ix, None)
1244
- self._dependencies[ix - 1] = self._dependences[ix]
1379
+ self._dependencies[typing.cast(int, ix) - 1] = self._dependencies[
1380
+ ix
1381
+ ]
1245
1382
  del self._dependencies[ix]
1246
1383
  for ix in sorted(self._unknowns.keys()):
1247
1384
  if ix > key:
1248
1385
  self._cache.pop(ix, None)
1249
- self._unknowns[ix - 1] = self._unknowns[ix]
1386
+ self._unknowns[typing.cast(int, ix) - 1] = self._unknowns[ix]
1250
1387
  del self._unknowns[ix]
1251
1388
  for ix in reversed(range(len(values.values))):
1252
1389
  if ix not in self._unknowns:
@@ -1364,8 +1501,8 @@ class Value:
1364
1501
  return unknowns
1365
1502
 
1366
1503
  @property
1367
- def _getDependencies(self):
1368
- dependencies = {}
1504
+ def _getDependencies(self) -> dict[str, str]:
1505
+ dependencies: dict[str, str] = {}
1369
1506
  for key, dependency in self._dependencies.items():
1370
1507
  dependencies[self._fullName(key)] = dependency._fullName()
1371
1508
  for key, unknown in self._unknowns.items():
@@ -1380,6 +1517,17 @@ class Value:
1380
1517
  dependencies.update(value._getDependencies)
1381
1518
  return dependencies
1382
1519
 
1520
+ def _setReadOnly(self, readOnly: bool | str):
1521
+ self._readOnly = readOnly
1522
+ if self._isMap:
1523
+ for key, value in self:
1524
+ if isinstance(value, Value):
1525
+ value._setReadOnly(readOnly)
1526
+ elif self._isList:
1527
+ for value in enumerate(self):
1528
+ if isinstance(value, Value):
1529
+ value._setReadOnly(readOnly)
1530
+
1383
1531
  def _patchUnknowns(self, patches):
1384
1532
  for key in list(self._unknowns.keys()):
1385
1533
  self[key] = patches[key]
@@ -1437,41 +1585,43 @@ def _formatObject(object, spec="yaml"):
1437
1585
 
1438
1586
 
1439
1587
  class _JSONEncoder(json.JSONEncoder):
1440
- def default(self, object):
1441
- if isinstance(object, (Message, MapMessage)):
1442
- if object:
1443
- return {key: value for key, value in object}
1588
+ def default(self, o):
1589
+ if isinstance(o, (Message, MapMessage)):
1590
+ if o:
1591
+ return {key: value for key, value in o}
1444
1592
  return None
1445
- if isinstance(object, RepeatedMessage):
1446
- if object:
1447
- return [value for value in object]
1593
+ if isinstance(o, RepeatedMessage):
1594
+ if o:
1595
+ return [value for value in o]
1448
1596
  return None
1449
- if isinstance(object, FieldMessage):
1450
- return object._value
1451
- if isinstance(object, Value):
1452
- match object._kind:
1453
- case "struct_value" | "Struc":
1454
- return {key: value for key, value in object}
1597
+ if isinstance(o, FieldMessage):
1598
+ return o._value
1599
+ if isinstance(o, Value):
1600
+ match o._kind:
1601
+ case "struct_value" | "Struct":
1602
+ return {key: value for key, value in o}
1455
1603
  case "list_value" | "ListValue":
1456
- return [value for value in object]
1604
+ return [value for value in o]
1457
1605
  case "string_value":
1458
- return object._value.string_value
1606
+ return o._value.string_value
1459
1607
  case "null_value":
1460
1608
  return None
1461
1609
  case "number_value":
1462
- value = object._value.number_value
1610
+ value = o._value.number_value
1463
1611
  if value.is_integer():
1464
1612
  value = int(value)
1465
1613
  return value
1466
1614
  case "bool_value":
1467
- return object._value.bool_value
1615
+ return o._value.bool_value
1468
1616
  case "Unknown":
1469
1617
  return "<<UNKNOWN>>"
1470
1618
  case _:
1471
1619
  return "<<UNEXPECTED>>"
1472
- if isinstance(object, datetime.datetime):
1473
- return object.isoformat()
1474
- return super().default(object)
1620
+ if isinstance(o, datetime.datetime):
1621
+ return o.isoformat()
1622
+ if isinstance(o, bytes):
1623
+ return o.decode("utf-8")
1624
+ return super().default(o)
1475
1625
 
1476
1626
 
1477
1627
  class _Dumper(yaml.SafeDumper):
@@ -1480,32 +1630,32 @@ class _Dumper(yaml.SafeDumper):
1480
1630
  "tag:yaml.org,2002:str", data, "|" if "\n" in data else None
1481
1631
  )
1482
1632
 
1483
- def represent_message_dict(self, message):
1633
+ def represent_message_dict(self, message: Message | MapMessage):
1484
1634
  return self.represent_dict({key: value for key, value in message})
1485
1635
 
1486
- def represent_message_list(self, messages):
1636
+ def represent_message_list(self, messages: RepeatedMessage):
1487
1637
  return self.represent_list([value for value in messages])
1488
1638
 
1489
- def represent_message_field(self, field):
1639
+ def represent_message_field(self, field: FieldMessage):
1490
1640
  if isinstance(field._value, str):
1491
1641
  return self.represent_str(field._value)
1492
1642
  if isinstance(field._value, bytes):
1493
1643
  return self.represent_binary(field._value)
1494
1644
  if field._value is None:
1495
- return self.represent_none()
1645
+ return self.represent_none(field._value)
1646
+ if isinstance(field._value, bool): # Must be before number check
1647
+ return self.represent_bool(bool(field._value))
1496
1648
  if isinstance(field._value, int):
1497
1649
  return self.represent_int(field._value)
1498
1650
  if isinstance(field._value, float):
1499
1651
  if field._value.is_integer():
1500
1652
  return self.represent_int(int(field._value))
1501
1653
  return self.represent_float(field._value)
1502
- if isinstance(field._value, bool):
1503
- return self.represent_bool(field._value)
1504
1654
  if field._value is _Unknown:
1505
1655
  return self.represent_str("<<UNKNOWN>>")
1506
1656
  return self.represent_str("<<UNEXPECTED>>")
1507
1657
 
1508
- def represent_value(self, value):
1658
+ def represent_value(self, value: Value):
1509
1659
  match value._kind:
1510
1660
  case "struct_value" | "Struct":
1511
1661
  return self.represent_dict({k: v for k, v in value})
@@ -1521,7 +1671,7 @@ class _Dumper(yaml.SafeDumper):
1521
1671
  return self.represent_int(int(value))
1522
1672
  return self.represent_float(value)
1523
1673
  case "bool_value":
1524
- return self.represent_bool(value._value.bool_value)
1674
+ return self.represent_bool(bool(value._value.bool_value))
1525
1675
  case "Unknown":
1526
1676
  return self.represent_str("<<UNKNOWN>>")
1527
1677
  case _: