protobuf 5.29.0rc3__py3-none-any.whl → 6.33.3__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.

Potentially problematic release.


This version of protobuf might be problematic. Click here for more details.

Files changed (45) hide show
  1. google/protobuf/__init__.py +1 -1
  2. google/protobuf/any.py +15 -1
  3. google/protobuf/any_pb2.py +5 -5
  4. google/protobuf/api_pb2.py +15 -11
  5. google/protobuf/compiler/plugin_pb2.py +5 -5
  6. google/protobuf/descriptor.py +413 -248
  7. google/protobuf/descriptor_database.py +22 -4
  8. google/protobuf/descriptor_pb2.py +320 -120
  9. google/protobuf/descriptor_pool.py +31 -13
  10. google/protobuf/duration_pb2.py +5 -5
  11. google/protobuf/empty_pb2.py +5 -5
  12. google/protobuf/field_mask_pb2.py +5 -5
  13. google/protobuf/internal/api_implementation.py +0 -6
  14. google/protobuf/internal/builder.py +4 -3
  15. google/protobuf/internal/containers.py +13 -0
  16. google/protobuf/internal/decoder.py +163 -133
  17. google/protobuf/internal/extension_dict.py +3 -3
  18. google/protobuf/internal/field_mask.py +6 -4
  19. google/protobuf/internal/python_edition_defaults.py +1 -1
  20. google/protobuf/internal/python_message.py +86 -70
  21. google/protobuf/internal/testing_refleaks.py +11 -2
  22. google/protobuf/internal/type_checkers.py +52 -5
  23. google/protobuf/internal/well_known_types.py +63 -46
  24. google/protobuf/json_format.py +113 -71
  25. google/protobuf/message.py +26 -0
  26. google/protobuf/message_factory.py +16 -63
  27. google/protobuf/proto.py +38 -1
  28. google/protobuf/proto_text.py +129 -0
  29. google/protobuf/reflection.py +0 -49
  30. google/protobuf/runtime_version.py +9 -29
  31. google/protobuf/source_context_pb2.py +5 -5
  32. google/protobuf/struct_pb2.py +5 -5
  33. google/protobuf/symbol_database.py +0 -18
  34. google/protobuf/text_format.py +49 -29
  35. google/protobuf/timestamp_pb2.py +5 -5
  36. google/protobuf/type_pb2.py +5 -5
  37. google/protobuf/unknown_fields.py +3 -4
  38. google/protobuf/wrappers_pb2.py +5 -5
  39. {protobuf-5.29.0rc3.dist-info → protobuf-6.33.3.dist-info}/METADATA +3 -3
  40. protobuf-6.33.3.dist-info/RECORD +58 -0
  41. google/protobuf/internal/_parameterized.py +0 -420
  42. google/protobuf/service.py +0 -213
  43. protobuf-5.29.0rc3.dist-info/RECORD +0 -59
  44. {protobuf-5.29.0rc3.dist-info → protobuf-6.33.3.dist-info}/LICENSE +0 -0
  45. {protobuf-5.29.0rc3.dist-info → protobuf-6.33.3.dist-info}/WHEEL +0 -0
@@ -20,13 +20,13 @@ __author__ = 'jieluo@google.com (Jie Luo)'
20
20
  import calendar
21
21
  import collections.abc
22
22
  import datetime
23
+ from typing import Union
23
24
  import warnings
24
25
  from google.protobuf.internal import field_mask
25
- from typing import Union
26
26
 
27
27
  FieldMask = field_mask.FieldMask
28
28
 
29
- _TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S'
29
+ _TIMESTAMPFORMAT = '%Y-%m-%dT%H:%M:%S'
30
30
  _NANOS_PER_SECOND = 1000000000
31
31
  _NANOS_PER_MILLISECOND = 1000000
32
32
  _NANOS_PER_MICROSECOND = 1000
@@ -48,8 +48,9 @@ class Any(object):
48
48
 
49
49
  __slots__ = ()
50
50
 
51
- def Pack(self, msg, type_url_prefix='type.googleapis.com/',
52
- deterministic=None):
51
+ def Pack(
52
+ self, msg, type_url_prefix='type.googleapis.com/', deterministic=None
53
+ ):
53
54
  """Packs the specified message into current Any message."""
54
55
  if len(type_url_prefix) < 1 or type_url_prefix[-1] != '/':
55
56
  self.type_url = '%s/%s' % (type_url_prefix, msg.DESCRIPTOR.full_name)
@@ -68,7 +69,7 @@ class Any(object):
68
69
  def TypeName(self):
69
70
  """Returns the protobuf type name of the inner message."""
70
71
  # Only last part is to be used: b/25630112
71
- return self.type_url.split('/')[-1]
72
+ return self.type_url.rpartition('/')[2]
72
73
 
73
74
  def Is(self, descriptor):
74
75
  """Checks if this Any represents the given protobuf type."""
@@ -113,8 +114,8 @@ class Timestamp(object):
113
114
 
114
115
  Args:
115
116
  value: A date string. Any fractional digits (or none) and any offset are
116
- accepted as long as they fit into nano-seconds precision.
117
- Example of accepted format: '1972-01-01T10:00:20.021-05:00'
117
+ accepted as long as they fit into nano-seconds precision. Example of
118
+ accepted format: '1972-01-01T10:00:20.021-05:00'
118
119
 
119
120
  Raises:
120
121
  ValueError: On parsing problems.
@@ -128,7 +129,8 @@ class Timestamp(object):
128
129
  timezone_offset = value.rfind('-')
129
130
  if timezone_offset == -1:
130
131
  raise ValueError(
131
- 'Failed to parse timestamp: missing valid timezone offset.')
132
+ 'Failed to parse timestamp: missing valid timezone offset.'
133
+ )
132
134
  time_value = value[0:timezone_offset]
133
135
  # Parse datetime and nanos.
134
136
  point_position = time_value.find('.')
@@ -137,18 +139,20 @@ class Timestamp(object):
137
139
  nano_value = ''
138
140
  else:
139
141
  second_value = time_value[:point_position]
140
- nano_value = time_value[point_position + 1:]
142
+ nano_value = time_value[point_position + 1 :]
141
143
  if 't' in second_value:
142
144
  raise ValueError(
143
- 'time data \'{0}\' does not match format \'%Y-%m-%dT%H:%M:%S\', '
144
- 'lowercase \'t\' is not accepted'.format(second_value))
145
- date_object = datetime.datetime.strptime(second_value, _TIMESTAMPFOMAT)
145
+ "time data '{0}' does not match format '%Y-%m-%dT%H:%M:%S', "
146
+ "lowercase 't' is not accepted".format(second_value)
147
+ )
148
+ date_object = datetime.datetime.strptime(second_value, _TIMESTAMPFORMAT)
146
149
  td = date_object - datetime.datetime(1970, 1, 1)
147
150
  seconds = td.seconds + td.days * _SECONDS_PER_DAY
148
151
  if len(nano_value) > 9:
149
152
  raise ValueError(
150
153
  'Failed to parse Timestamp: nanos {0} more than '
151
- '9 fractional digits.'.format(nano_value))
154
+ '9 fractional digits.'.format(nano_value)
155
+ )
152
156
  if nano_value:
153
157
  nanos = round(float('0.' + nano_value) * 1e9)
154
158
  else:
@@ -156,18 +160,20 @@ class Timestamp(object):
156
160
  # Parse timezone offsets.
157
161
  if value[timezone_offset] == 'Z':
158
162
  if len(value) != timezone_offset + 1:
159
- raise ValueError('Failed to parse timestamp: invalid trailing'
160
- ' data {0}.'.format(value))
163
+ raise ValueError(
164
+ 'Failed to parse timestamp: invalid trailing data {0}.'.format(
165
+ value
166
+ )
167
+ )
161
168
  else:
162
169
  timezone = value[timezone_offset:]
163
170
  pos = timezone.find(':')
164
171
  if pos == -1:
165
- raise ValueError(
166
- 'Invalid timezone offset value: {0}.'.format(timezone))
172
+ raise ValueError('Invalid timezone offset value: {0}.'.format(timezone))
167
173
  if timezone[0] == '+':
168
- seconds -= (int(timezone[1:pos])*60+int(timezone[pos+1:]))*60
174
+ seconds -= (int(timezone[1:pos]) * 60 + int(timezone[pos + 1 :])) * 60
169
175
  else:
170
- seconds += (int(timezone[1:pos])*60+int(timezone[pos+1:]))*60
176
+ seconds += (int(timezone[1:pos]) * 60 + int(timezone[pos + 1 :])) * 60
171
177
  # Set seconds and nanos
172
178
  _CheckTimestampValid(seconds, nanos)
173
179
  self.seconds = int(seconds)
@@ -175,7 +181,7 @@ class Timestamp(object):
175
181
 
176
182
  def GetCurrentTime(self):
177
183
  """Get the current UTC into Timestamp."""
178
- self.FromDatetime(datetime.datetime.utcnow())
184
+ self.FromDatetime(datetime.datetime.now(tz=datetime.timezone.utc))
179
185
 
180
186
  def ToNanoseconds(self):
181
187
  """Converts Timestamp to nanoseconds since epoch."""
@@ -185,14 +191,16 @@ class Timestamp(object):
185
191
  def ToMicroseconds(self):
186
192
  """Converts Timestamp to microseconds since epoch."""
187
193
  _CheckTimestampValid(self.seconds, self.nanos)
188
- return (self.seconds * _MICROS_PER_SECOND +
189
- self.nanos // _NANOS_PER_MICROSECOND)
194
+ return (
195
+ self.seconds * _MICROS_PER_SECOND + self.nanos // _NANOS_PER_MICROSECOND
196
+ )
190
197
 
191
198
  def ToMilliseconds(self):
192
199
  """Converts Timestamp to milliseconds since epoch."""
193
200
  _CheckTimestampValid(self.seconds, self.nanos)
194
- return (self.seconds * _MILLIS_PER_SECOND +
195
- self.nanos // _NANOS_PER_MILLISECOND)
201
+ return (
202
+ self.seconds * _MILLIS_PER_SECOND + self.nanos // _NANOS_PER_MILLISECOND
203
+ )
196
204
 
197
205
  def ToSeconds(self):
198
206
  """Converts Timestamp to seconds since epoch."""
@@ -312,7 +320,8 @@ def _CheckTimestampValid(seconds, nanos):
312
320
  if nanos < 0 or nanos >= _NANOS_PER_SECOND:
313
321
  raise ValueError(
314
322
  'Timestamp is not valid: Nanos {} must be in a range '
315
- '[0, 999999].'.format(nanos))
323
+ '[0, 999999].'.format(nanos)
324
+ )
316
325
 
317
326
 
318
327
  class Duration(object):
@@ -332,7 +341,7 @@ class Duration(object):
332
341
  _CheckDurationValid(self.seconds, self.nanos)
333
342
  if self.seconds < 0 or self.nanos < 0:
334
343
  result = '-'
335
- seconds = - self.seconds + int((0 - self.nanos) // 1e9)
344
+ seconds = -self.seconds + int((0 - self.nanos) // 1e9)
336
345
  nanos = (0 - self.nanos) % 1e9
337
346
  else:
338
347
  result = ''
@@ -357,8 +366,8 @@ class Duration(object):
357
366
 
358
367
  Args:
359
368
  value: A string to be converted. The string must end with 's'. Any
360
- fractional digits (or none) are accepted as long as they fit into
361
- precision. For example: "1s", "1.01s", "1.0000001s", "-3.100s
369
+ fractional digits (or none) are accepted as long as they fit into
370
+ precision. For example: "1s", "1.01s", "1.0000001s", "-3.100s
362
371
 
363
372
  Raises:
364
373
  ValueError: On parsing problems.
@@ -366,8 +375,7 @@ class Duration(object):
366
375
  if not isinstance(value, str):
367
376
  raise ValueError('Duration JSON value not a string: {!r}'.format(value))
368
377
  if len(value) < 1 or value[-1] != 's':
369
- raise ValueError(
370
- 'Duration must end with letter "s": {0}.'.format(value))
378
+ raise ValueError('Duration must end with letter "s": {0}.'.format(value))
371
379
  try:
372
380
  pos = value.find('.')
373
381
  if pos == -1:
@@ -376,15 +384,14 @@ class Duration(object):
376
384
  else:
377
385
  seconds = int(value[:pos])
378
386
  if value[0] == '-':
379
- nanos = int(round(float('-0{0}'.format(value[pos: -1])) *1e9))
387
+ nanos = int(round(float('-0{0}'.format(value[pos:-1])) * 1e9))
380
388
  else:
381
- nanos = int(round(float('0{0}'.format(value[pos: -1])) *1e9))
389
+ nanos = int(round(float('0{0}'.format(value[pos:-1])) * 1e9))
382
390
  _CheckDurationValid(seconds, nanos)
383
391
  self.seconds = seconds
384
392
  self.nanos = nanos
385
393
  except ValueError as e:
386
- raise ValueError(
387
- 'Couldn\'t parse duration: {0} : {1}.'.format(value, e))
394
+ raise ValueError("Couldn't parse duration: {0} : {1}.".format(value, e))
388
395
 
389
396
  def ToNanoseconds(self):
390
397
  """Converts a Duration to nanoseconds."""
@@ -406,20 +413,23 @@ class Duration(object):
406
413
 
407
414
  def FromNanoseconds(self, nanos):
408
415
  """Converts nanoseconds to Duration."""
409
- self._NormalizeDuration(nanos // _NANOS_PER_SECOND,
410
- nanos % _NANOS_PER_SECOND)
416
+ self._NormalizeDuration(
417
+ nanos // _NANOS_PER_SECOND, nanos % _NANOS_PER_SECOND
418
+ )
411
419
 
412
420
  def FromMicroseconds(self, micros):
413
421
  """Converts microseconds to Duration."""
414
422
  self._NormalizeDuration(
415
423
  micros // _MICROS_PER_SECOND,
416
- (micros % _MICROS_PER_SECOND) * _NANOS_PER_MICROSECOND)
424
+ (micros % _MICROS_PER_SECOND) * _NANOS_PER_MICROSECOND,
425
+ )
417
426
 
418
427
  def FromMilliseconds(self, millis):
419
428
  """Converts milliseconds to Duration."""
420
429
  self._NormalizeDuration(
421
430
  millis // _MILLIS_PER_SECOND,
422
- (millis % _MILLIS_PER_SECOND) * _NANOS_PER_MILLISECOND)
431
+ (millis % _MILLIS_PER_SECOND) * _NANOS_PER_MILLISECOND,
432
+ )
423
433
 
424
434
  def FromSeconds(self, seconds):
425
435
  """Converts seconds to Duration."""
@@ -429,8 +439,9 @@ class Duration(object):
429
439
  def ToTimedelta(self) -> datetime.timedelta:
430
440
  """Converts Duration to timedelta."""
431
441
  return datetime.timedelta(
432
- seconds=self.seconds, microseconds=_RoundTowardZero(
433
- self.nanos, _NANOS_PER_MICROSECOND))
442
+ seconds=self.seconds,
443
+ microseconds=_RoundTowardZero(self.nanos, _NANOS_PER_MICROSECOND),
444
+ )
434
445
 
435
446
  def FromTimedelta(self, td):
436
447
  """Converts timedelta to Duration."""
@@ -464,22 +475,26 @@ class Duration(object):
464
475
 
465
476
  __radd__ = __add__
466
477
 
467
- def __rsub__(self, dt) -> Union[datetime.datetime, datetime.timedelta]:
468
- return dt - self.ToTimedelta()
478
+ def __sub__(self, value) -> datetime.timedelta:
479
+ return self.ToTimedelta() - value
480
+
481
+ def __rsub__(self, value) -> Union[datetime.datetime, datetime.timedelta]:
482
+ return value - self.ToTimedelta()
469
483
 
470
484
 
471
485
  def _CheckDurationValid(seconds, nanos):
472
486
  if seconds < -_DURATION_SECONDS_MAX or seconds > _DURATION_SECONDS_MAX:
473
487
  raise ValueError(
474
488
  'Duration is not valid: Seconds {0} must be in range '
475
- '[-315576000000, 315576000000].'.format(seconds))
489
+ '[-315576000000, 315576000000].'.format(seconds)
490
+ )
476
491
  if nanos <= -_NANOS_PER_SECOND or nanos >= _NANOS_PER_SECOND:
477
492
  raise ValueError(
478
493
  'Duration is not valid: Nanos {0} must be in range '
479
- '[-999999999, 999999999].'.format(nanos))
494
+ '[-999999999, 999999999].'.format(nanos)
495
+ )
480
496
  if (nanos < 0 and seconds > 0) or (nanos > 0 and seconds < 0):
481
- raise ValueError(
482
- 'Duration is not valid: Sign mismatch.')
497
+ raise ValueError('Duration is not valid: Sign mismatch.')
483
498
 
484
499
 
485
500
  def _RoundTowardZero(value, divider):
@@ -601,6 +616,7 @@ class Struct(object):
601
616
  for key, value in dictionary.items():
602
617
  _SetStructValue(self.fields[key], value)
603
618
 
619
+
604
620
  collections.abc.MutableMapping.register(Struct)
605
621
 
606
622
 
@@ -663,6 +679,7 @@ class ListValue(object):
663
679
  list_value.Clear()
664
680
  return list_value
665
681
 
682
+
666
683
  collections.abc.MutableSequence.register(ListValue)
667
684
 
668
685
 
@@ -26,6 +26,7 @@ import json
26
26
  import math
27
27
  from operator import methodcaller
28
28
  import re
29
+ import warnings
29
30
 
30
31
  from google.protobuf import descriptor
31
32
  from google.protobuf import message_factory
@@ -72,6 +73,7 @@ class ParseError(Error):
72
73
 
73
74
  class EnumStringValueParseError(ParseError):
74
75
  """Thrown if unknown string enum value is encountered.
76
+
75
77
  This exception is suppressed if ignore_unknown_fields is set.
76
78
  """
77
79
 
@@ -91,10 +93,10 @@ def MessageToJson(
91
93
 
92
94
  Args:
93
95
  message: The protocol buffers message instance to serialize.
94
- always_print_fields_with_no_presence: If True, fields without
95
- presence (implicit presence scalars, repeated fields, and map fields) will
96
- always be serialized. Any field that supports presence is not affected by
97
- this option (including singular message fields and oneof fields).
96
+ always_print_fields_with_no_presence: If True, fields without presence
97
+ (implicit presence scalars, repeated fields, and map fields) will always
98
+ be serialized. Any field that supports presence is not affected by this
99
+ option (including singular message fields and oneof fields).
98
100
  preserving_proto_field_name: If True, use the original proto field names as
99
101
  defined in the .proto file. If False, convert the field names to
100
102
  lowerCamelCase.
@@ -105,7 +107,8 @@ def MessageToJson(
105
107
  use_integers_for_enums: If true, print integers instead of enum names.
106
108
  descriptor_pool: A Descriptor Pool for resolving types. If None use the
107
109
  default.
108
- float_precision: If set, use this to specify float field valid digits.
110
+ float_precision: Deprecated. If set, use this to specify float field valid
111
+ digits.
109
112
  ensure_ascii: If True, strings with non-ASCII characters are escaped. If
110
113
  False, Unicode strings are returned unchanged.
111
114
 
@@ -117,7 +120,7 @@ def MessageToJson(
117
120
  use_integers_for_enums,
118
121
  descriptor_pool,
119
122
  float_precision,
120
- always_print_fields_with_no_presence
123
+ always_print_fields_with_no_presence,
121
124
  )
122
125
  return printer.ToJsonString(message, indent, sort_keys, ensure_ascii)
123
126
 
@@ -136,17 +139,18 @@ def MessageToDict(
136
139
 
137
140
  Args:
138
141
  message: The protocol buffers message instance to serialize.
139
- always_print_fields_with_no_presence: If True, fields without
140
- presence (implicit presence scalars, repeated fields, and map fields) will
141
- always be serialized. Any field that supports presence is not affected by
142
- this option (including singular message fields and oneof fields).
142
+ always_print_fields_with_no_presence: If True, fields without presence
143
+ (implicit presence scalars, repeated fields, and map fields) will always
144
+ be serialized. Any field that supports presence is not affected by this
145
+ option (including singular message fields and oneof fields).
143
146
  preserving_proto_field_name: If True, use the original proto field names as
144
147
  defined in the .proto file. If False, convert the field names to
145
148
  lowerCamelCase.
146
149
  use_integers_for_enums: If true, print integers instead of enum names.
147
150
  descriptor_pool: A Descriptor Pool for resolving types. If None use the
148
151
  default.
149
- float_precision: If set, use this to specify float field valid digits.
152
+ float_precision: Deprecated. If set, use this to specify float field valid
153
+ digits.
150
154
 
151
155
  Returns:
152
156
  A dict representation of the protocol buffer message.
@@ -188,6 +192,11 @@ class _Printer(object):
188
192
  self.use_integers_for_enums = use_integers_for_enums
189
193
  self.descriptor_pool = descriptor_pool
190
194
  if float_precision:
195
+ warnings.warn(
196
+ 'float_precision option is deprecated for json_format. '
197
+ 'This will turn into error in 7.34.0, please remove it '
198
+ 'before that.'
199
+ )
191
200
  self.float_format = '.{}g'.format(float_precision)
192
201
  else:
193
202
  self.float_format = None
@@ -215,10 +224,13 @@ class _Printer(object):
215
224
 
216
225
  try:
217
226
  for field, value in fields:
218
- if self.preserving_proto_field_name:
227
+ if field.is_extension:
228
+ name = '[%s]' % field.full_name
229
+ elif self.preserving_proto_field_name:
219
230
  name = field.name
220
231
  else:
221
232
  name = field.json_name
233
+
222
234
  if _IsMapEntry(field):
223
235
  # Convert a map field.
224
236
  v_field = field.message_type.fields_by_name['value']
@@ -233,12 +245,9 @@ class _Printer(object):
233
245
  recorded_key = str(key)
234
246
  js_map[recorded_key] = self._FieldToJsonObject(v_field, value[key])
235
247
  js[name] = js_map
236
- elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
248
+ elif field.is_repeated:
237
249
  # Convert a repeated field.
238
250
  js[name] = [self._FieldToJsonObject(field, k) for k in value]
239
- elif field.is_extension:
240
- name = '[%s]' % field.full_name
241
- js[name] = self._FieldToJsonObject(field, value)
242
251
  else:
243
252
  js[name] = self._FieldToJsonObject(field, value)
244
253
 
@@ -251,10 +260,7 @@ class _Printer(object):
251
260
 
252
261
  # always_print_fields_with_no_presence doesn't apply to
253
262
  # any field which supports presence.
254
- if (
255
- self.always_print_fields_with_no_presence
256
- and field.has_presence
257
- ):
263
+ if self.always_print_fields_with_no_presence and field.has_presence:
258
264
  continue
259
265
 
260
266
  if self.preserving_proto_field_name:
@@ -266,7 +272,7 @@ class _Printer(object):
266
272
  continue
267
273
  if _IsMapEntry(field):
268
274
  js[name] = {}
269
- elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
275
+ elif field.is_repeated:
270
276
  js[name] = []
271
277
  else:
272
278
  js[name] = self._FieldToJsonObject(field, field.default_value)
@@ -316,11 +322,10 @@ class _Printer(object):
316
322
  return _INFINITY
317
323
  if math.isnan(value):
318
324
  return _NAN
319
- if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:
320
- if self.float_format:
321
- return float(format(value, self.float_format))
322
- else:
323
- return type_checkers.ToShortestFloat(value)
325
+ if self.float_format:
326
+ return float(format(value, self.float_format))
327
+ elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT:
328
+ return type_checkers.ToShortestFloat(value)
324
329
 
325
330
  return value
326
331
 
@@ -497,6 +502,7 @@ def ParseDict(
497
502
 
498
503
 
499
504
  _INT_OR_FLOAT = (int, float)
505
+ _LIST_LIKE = (list, tuple)
500
506
 
501
507
 
502
508
  class _Parser(object):
@@ -556,6 +562,25 @@ class _Parser(object):
556
562
  fields_by_json_name = dict(
557
563
  (f.json_name, f) for f in message_descriptor.fields
558
564
  )
565
+
566
+ def _ClearFieldOrExtension(message, field):
567
+ if field.is_extension:
568
+ message.ClearExtension(field)
569
+ else:
570
+ message.ClearField(field.name)
571
+
572
+ def _GetFieldOrExtension(message, field):
573
+ if field.is_extension:
574
+ return message.Extensions[field]
575
+ else:
576
+ return getattr(message, field.name)
577
+
578
+ def _SetFieldOrExtension(message, field, value):
579
+ if field.is_extension:
580
+ message.Extensions[field] = value
581
+ else:
582
+ setattr(message, field.name, value)
583
+
559
584
  for name in js:
560
585
  try:
561
586
  field = fields_by_json_name.get(name, None)
@@ -619,26 +644,26 @@ class _Parser(object):
619
644
  field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE
620
645
  and field.message_type.full_name == 'google.protobuf.Value'
621
646
  ):
622
- sub_message = getattr(message, field.name)
647
+ sub_message = _GetFieldOrExtension(message, field)
623
648
  sub_message.null_value = 0
624
649
  elif (
625
650
  field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM
626
651
  and field.enum_type.full_name == 'google.protobuf.NullValue'
627
652
  ):
628
- setattr(message, field.name, 0)
653
+ _SetFieldOrExtension(message, field, 0)
629
654
  else:
630
- message.ClearField(field.name)
655
+ _ClearFieldOrExtension(message, field)
631
656
  continue
632
657
 
633
658
  # Parse field value.
634
659
  if _IsMapEntry(field):
635
- message.ClearField(field.name)
660
+ _ClearFieldOrExtension(message, field)
636
661
  self._ConvertMapFieldValue(
637
662
  value, message, field, '{0}.{1}'.format(path, name)
638
663
  )
639
- elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED:
640
- message.ClearField(field.name)
641
- if not isinstance(value, list):
664
+ elif field.is_repeated:
665
+ _ClearFieldOrExtension(message, field)
666
+ if not isinstance(value, _LIST_LIKE):
642
667
  raise ParseError(
643
668
  'repeated field {0} must be in [] which is {1} at {2}'.format(
644
669
  name, value, path
@@ -647,7 +672,7 @@ class _Parser(object):
647
672
  if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
648
673
  # Repeated message field.
649
674
  for index, item in enumerate(value):
650
- sub_message = getattr(message, field.name).add()
675
+ sub_message = _GetFieldOrExtension(message, field).add()
651
676
  # None is a null_value in Value.
652
677
  if (
653
678
  item is None
@@ -674,19 +699,16 @@ class _Parser(object):
674
699
  )
675
700
  )
676
701
  self._ConvertAndAppendScalar(
677
- message, field, item, '{0}.{1}[{2}]'.format(path, name, index))
702
+ message, field, item, '{0}.{1}[{2}]'.format(path, name, index)
703
+ )
678
704
  elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE:
679
- if field.is_extension:
680
- sub_message = message.Extensions[field]
681
- else:
682
- sub_message = getattr(message, field.name)
705
+ sub_message = _GetFieldOrExtension(message, field)
683
706
  sub_message.SetInParent()
684
707
  self.ConvertMessage(value, sub_message, '{0}.{1}'.format(path, name))
685
708
  else:
686
- if field.is_extension:
687
- self._ConvertAndSetScalarExtension(message, field, value, '{0}.{1}'.format(path, name))
688
- else:
689
- self._ConvertAndSetScalar(message, field, value, '{0}.{1}'.format(path, name))
709
+ self._ConvertAndSetScalar(
710
+ message, field, value, '{0}.{1}'.format(path, name)
711
+ )
690
712
  except ParseError as e:
691
713
  if field and field.containing_oneof is None:
692
714
  raise ParseError(
@@ -733,8 +755,10 @@ class _Parser(object):
733
755
  )(self)
734
756
  else:
735
757
  del value['@type']
736
- self._ConvertFieldValuePair(value, sub_message, path)
737
- value['@type'] = type_url
758
+ try:
759
+ self._ConvertFieldValuePair(value, sub_message, path)
760
+ finally:
761
+ value['@type'] = type_url
738
762
  # Sets Any message
739
763
  message.value = sub_message.SerializeToString()
740
764
  message.type_url = type_url
@@ -752,8 +776,8 @@ class _Parser(object):
752
776
  """Convert a JSON representation into Value message."""
753
777
  if isinstance(value, dict):
754
778
  self._ConvertStructMessage(value, message.struct_value, path)
755
- elif isinstance(value, list):
756
- self._ConvertListValueMessage(value, message.list_value, path)
779
+ elif isinstance(value, _LIST_LIKE):
780
+ self._ConvertListOrTupleValueMessage(value, message.list_value, path)
757
781
  elif value is None:
758
782
  message.null_value = 0
759
783
  elif isinstance(value, bool):
@@ -769,9 +793,9 @@ class _Parser(object):
769
793
  )
770
794
  )
771
795
 
772
- def _ConvertListValueMessage(self, value, message, path):
796
+ def _ConvertListOrTupleValueMessage(self, value, message, path):
773
797
  """Convert a JSON representation into ListValue message."""
774
- if not isinstance(value, list):
798
+ if not isinstance(value, _LIST_LIKE):
775
799
  raise ParseError(
776
800
  'ListValue must be in [] which is {0} at {1}'.format(value, path)
777
801
  )
@@ -799,7 +823,9 @@ class _Parser(object):
799
823
  def _ConvertWrapperMessage(self, value, message, path):
800
824
  """Convert a JSON representation into Wrapper message."""
801
825
  field = message.DESCRIPTOR.fields_by_name['value']
802
- self._ConvertAndSetScalar(message, field, value, path='{0}.value'.format(path))
826
+ self._ConvertAndSetScalar(
827
+ message, field, value, path='{0}.value'.format(path)
828
+ )
803
829
 
804
830
  def _ConvertMapFieldValue(self, value, message, field, path):
805
831
  """Convert map field value for a message map field.
@@ -837,24 +863,17 @@ class _Parser(object):
837
863
  field,
838
864
  key_value,
839
865
  value[key],
840
- path='{0}[{1}]'.format(path, key_value))
841
-
842
- def _ConvertAndSetScalarExtension(self, message, extension_field, js_value, path):
843
- """Convert scalar from js_value and assign it to message.Extensions[extension_field]."""
844
- try:
845
- message.Extensions[extension_field] = _ConvertScalarFieldValue(
846
- js_value, extension_field, path)
847
- except EnumStringValueParseError:
848
- if not self.ignore_unknown_fields:
849
- raise
866
+ path='{0}[{1}]'.format(path, key_value),
867
+ )
850
868
 
851
869
  def _ConvertAndSetScalar(self, message, field, js_value, path):
852
870
  """Convert scalar from js_value and assign it to message.field."""
853
871
  try:
854
- setattr(
855
- message,
856
- field.name,
857
- _ConvertScalarFieldValue(js_value, field, path))
872
+ value = _ConvertScalarFieldValue(js_value, field, path)
873
+ if field.is_extension:
874
+ message.Extensions[field] = value
875
+ else:
876
+ setattr(message, field.name, value)
858
877
  except EnumStringValueParseError:
859
878
  if not self.ignore_unknown_fields:
860
879
  raise
@@ -862,17 +881,27 @@ class _Parser(object):
862
881
  def _ConvertAndAppendScalar(self, message, repeated_field, js_value, path):
863
882
  """Convert scalar from js_value and append it to message.repeated_field."""
864
883
  try:
865
- getattr(message, repeated_field.name).append(
866
- _ConvertScalarFieldValue(js_value, repeated_field, path))
884
+ if repeated_field.is_extension:
885
+ repeated = message.Extensions[repeated_field]
886
+ else:
887
+ repeated = getattr(message, repeated_field.name)
888
+ value = _ConvertScalarFieldValue(js_value, repeated_field, path)
889
+ repeated.append(value)
867
890
  except EnumStringValueParseError:
868
891
  if not self.ignore_unknown_fields:
869
892
  raise
870
893
 
871
- def _ConvertAndSetScalarToMapKey(self, message, map_field, converted_key, js_value, path):
894
+ def _ConvertAndSetScalarToMapKey(
895
+ self, message, map_field, converted_key, js_value, path
896
+ ):
872
897
  """Convert scalar from 'js_value' and add it to message.map_field[converted_key]."""
873
898
  try:
874
- getattr(message, map_field.name)[converted_key] = _ConvertScalarFieldValue(
875
- js_value, map_field.message_type.fields_by_name['value'], path,
899
+ getattr(message, map_field.name)[converted_key] = (
900
+ _ConvertScalarFieldValue(
901
+ js_value,
902
+ map_field.message_type.fields_by_name['value'],
903
+ path,
904
+ )
876
905
  )
877
906
  except EnumStringValueParseError:
878
907
  if not self.ignore_unknown_fields:
@@ -971,7 +1000,20 @@ def _ConvertInteger(value):
971
1000
  'Bool value {0} is not acceptable for integer field'.format(value)
972
1001
  )
973
1002
 
974
- return int(value)
1003
+ try:
1004
+ return int(value)
1005
+ except ValueError as e:
1006
+ # Attempt to parse as an integer-valued float.
1007
+ try:
1008
+ f = float(value)
1009
+ except ValueError:
1010
+ # Raise the original exception for the int parse.
1011
+ raise e # pylint: disable=raise-missing-from
1012
+ if not f.is_integer():
1013
+ raise ParseError(
1014
+ 'Couldn\'t parse non-integer string: "{0}"'.format(value)
1015
+ ) from e
1016
+ return int(f)
975
1017
 
976
1018
 
977
1019
  def _ConvertFloat(value, field):
@@ -1052,7 +1094,7 @@ _WKTJSONMETHODS = {
1052
1094
  ],
1053
1095
  'google.protobuf.ListValue': [
1054
1096
  '_ListValueMessageToJsonObject',
1055
- '_ConvertListValueMessage',
1097
+ '_ConvertListOrTupleValueMessage',
1056
1098
  ],
1057
1099
  'google.protobuf.Struct': [
1058
1100
  '_StructMessageToJsonObject',