Skip to content

Commit 6f128a7

Browse files
authored
Use proto-json format when serializing enums and durations (#800)
Backend already supports `camelCase`, `PascalCase`, `snake_case` and `UPPER_SNAKE_CASE` when deserializing enum values so this change should be safe. Duration isn't used in any existing method so it is also a safe change. Backend expects the proto-json standard where duration is serialized as "<seconds>.<nanos>s".
1 parent a7a8566 commit 6f128a7

File tree

1 file changed

+35
-25
lines changed

1 file changed

+35
-25
lines changed

src/kagglesdk/kaggle_object.py

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -55,30 +55,12 @@ class Foo(Enum):
5555
def _to_str(cls, v):
5656
# "v" corresponds to an enum instance: Example foo or Foo.Test above.
5757
# "cls" corresponds to the enum type Foo above.
58-
# enum_prefix = f'{_pascal_case_to_upper_snake_case(cls.__name__)}_'
59-
# if v.name.startswith(enum_prefix):
60-
# return v.name
61-
# return f'{enum_prefix}{v.name}'
62-
enum_prefix = f'{_pascal_case_to_upper_snake_case(cls.__name__)}_'
63-
if v.name.find(enum_prefix) == 0:
64-
return v.name[len(enum_prefix) :].lower()
6558
return v.name
6659

6760
@staticmethod
6861
def _from_str(cls, v):
6962
# "v" corresponds to enum string: Example "TEST" above.
7063
# "cls" corresponds to the enum type Foo above.
71-
# enum_items = {item.name: item for item in cls}
72-
# if v in enum_items:
73-
# return enum_items[v]
74-
#
75-
# # Try with enum prefix. Example: EnvironmentType.JSON -> "ENVIRONMENT_TYPE_JSON"
76-
# enum_prefix = _pascal_case_to_upper_snake_case(cls.__name__)
77-
# if v.startswith(enum_prefix):
78-
# ix_start = len(enum_prefix) + 1
79-
# return enum_items[v[ix_start:]]
80-
#
81-
# return enum_items[f'{enum_prefix}_{v}']
8264
try:
8365
return cls[v]
8466
except KeyError:
@@ -159,9 +141,22 @@ def _from_str(v):
159141

160142

161143
class TimeDeltaSerializer(ObjectSerializer):
144+
# Scaling factors idea from https://github.com/protocolbuffers/protobuf/blob/master/csharp/src/Google.Protobuf/JsonParser.cs
145+
SUBSECOND_SCALING_FACTORS = [
146+
0,
147+
100_000_000,
148+
10_000_000,
149+
1_000_000,
150+
100_000,
151+
10_000,
152+
1_000,
153+
100,
154+
10,
155+
1,
156+
]
162157

163158
def __init__(self):
164-
"""Time deltas are serialized/deserialized as a string in "mm:ss" format"""
159+
"""Time deltas are serialized/deserialized as a string in "<seconds>.<nanoseconds>s" format. Example: 151.500s"""
165160
ObjectSerializer.__init__(
166161
self,
167162
lambda cls, t, _: TimeDeltaSerializer._to_dict_value(t),
@@ -170,15 +165,30 @@ def __init__(self):
170165

171166
@staticmethod
172167
def _to_dict_value(delta):
173-
seconds = int(delta.total_seconds())
174-
minutes = seconds // 60
175-
seconds -= minutes * 60
176-
return '{}:{:02}'.format(int(minutes), int(seconds))
168+
seconds = delta.seconds
169+
nanos_str = TimeDeltaSerializer._nanos_to_str(delta.microseconds * 1000)
170+
if nanos_str is None:
171+
return "{}s".format(seconds)
172+
return "{}.{}s".format(seconds, nanos_str)
173+
174+
@staticmethod
175+
def _nanos_to_str(nanos):
176+
if nanos == 0:
177+
return None
178+
if nanos < 0:
179+
nanos *= -1
180+
if nanos % 1000000 == 0:
181+
return "{}".format(nanos / 1000000)
182+
elif nanos % 1000 == 0:
183+
return "{}".format(nanos / 1000)
184+
else:
185+
return "{}".format(nanos)
177186

178187
@staticmethod
179188
def _from_dict_value(value):
180-
(minutes, seconds) = value.split(':')
181-
return timedelta(minutes=int(minutes), seconds=int(seconds))
189+
(seconds, nanosRaw) = value.rstrip('s').split('.')
190+
nanos = int(nanosRaw) * TimeDeltaSerializer.SUBSECOND_SCALING_FACTORS[len(nanosRaw)]
191+
return timedelta(seconds=int(seconds), microseconds=int(int(nanos) / 1000))
182192

183193

184194
class FieldMaskSerializer(ObjectSerializer):

0 commit comments

Comments
 (0)