8
8
import google .protobuf .duration_pb2
9
9
import google .protobuf .empty_pb2
10
10
import google .protobuf .message
11
+ import google .protobuf .struct_pb2
11
12
import google .protobuf .timestamp_pb2
12
13
import google .protobuf .wrappers_pb2
13
14
from google .protobuf import descriptor_pool , message_factory
@@ -44,6 +45,12 @@ def marshal_any(value: Any) -> google.protobuf.any_pb2.Any:
44
45
nanos = value .microseconds * 1000
45
46
value = google .protobuf .duration_pb2 .Duration (seconds = seconds , nanos = nanos )
46
47
48
+ if isinstance (value , list ) or isinstance (value , dict ):
49
+ try :
50
+ value = as_struct_value (value )
51
+ except ValueError :
52
+ pass # fallthrough
53
+
47
54
if not isinstance (value , google .protobuf .message .Message ):
48
55
value = pickled_pb .Pickled (pickled_value = pickle .dumps (value ))
49
56
@@ -64,34 +71,100 @@ def unmarshal_any(any: google.protobuf.any_pb2.Any) -> Any:
64
71
65
72
if isinstance (proto , pickled_pb .Pickled ):
66
73
return pickle .loads (proto .pickled_value )
74
+
67
75
elif isinstance (proto , google .protobuf .empty_pb2 .Empty ):
68
76
return None
77
+
69
78
elif isinstance (proto , google .protobuf .wrappers_pb2 .BoolValue ):
70
79
return proto .value
80
+
71
81
elif isinstance (proto , google .protobuf .wrappers_pb2 .Int32Value ):
72
82
return proto .value
83
+
73
84
elif isinstance (proto , google .protobuf .wrappers_pb2 .Int64Value ):
74
85
return proto .value
86
+
75
87
elif isinstance (proto , google .protobuf .wrappers_pb2 .UInt32Value ):
76
88
return proto .value
89
+
77
90
elif isinstance (proto , google .protobuf .wrappers_pb2 .UInt64Value ):
78
91
return proto .value
92
+
79
93
elif isinstance (proto , google .protobuf .wrappers_pb2 .FloatValue ):
80
94
return proto .value
95
+
81
96
elif isinstance (proto , google .protobuf .wrappers_pb2 .DoubleValue ):
82
97
return proto .value
98
+
83
99
elif isinstance (proto , google .protobuf .wrappers_pb2 .StringValue ):
84
100
return proto .value
101
+
85
102
elif isinstance (proto , google .protobuf .wrappers_pb2 .BytesValue ):
86
103
try :
87
104
# Assume it's the legacy container for pickled values.
88
105
return pickle .loads (proto .value )
89
106
except Exception as e :
90
107
# Otherwise, return the literal bytes.
91
108
return proto .value
109
+
92
110
elif isinstance (proto , google .protobuf .timestamp_pb2 .Timestamp ):
93
111
return proto .ToDatetime (tzinfo = UTC )
112
+
94
113
elif isinstance (proto , google .protobuf .duration_pb2 .Duration ):
95
114
return proto .ToTimedelta ()
96
115
116
+ elif isinstance (proto , google .protobuf .struct_pb2 .Value ):
117
+ return from_struct_value (proto )
118
+
97
119
return proto
120
+
121
+
122
+ def as_struct_value (value : Any ) -> google .protobuf .struct_pb2 .Value :
123
+ if value is None :
124
+ null_value = google .protobuf .struct_pb2 .NullValue .NULL_VALUE
125
+ return google .protobuf .struct_pb2 .Value (null_value = null_value )
126
+
127
+ elif isinstance (value , bool ):
128
+ return google .protobuf .struct_pb2 .Value (bool_value = value )
129
+
130
+ elif isinstance (value , int ) or isinstance (value , float ):
131
+ return google .protobuf .struct_pb2 .Value (number_value = float (value ))
132
+
133
+ elif isinstance (value , str ):
134
+ return google .protobuf .struct_pb2 .Value (string_value = value )
135
+
136
+ elif isinstance (value , list ):
137
+ list_value = google .protobuf .struct_pb2 .ListValue (
138
+ values = [as_struct_value (v ) for v in value ]
139
+ )
140
+ return google .protobuf .struct_pb2 .Value (list_value = list_value )
141
+
142
+ elif isinstance (value , dict ):
143
+ for key in value .keys ():
144
+ if not isinstance (key , str ):
145
+ raise ValueError ("unsupported object key" )
146
+
147
+ struct_value = google .protobuf .struct_pb2 .Struct (
148
+ fields = {k : as_struct_value (v ) for k , v in value .items ()}
149
+ )
150
+ return google .protobuf .struct_pb2 .Value (struct_value = struct_value )
151
+
152
+ raise ValueError ("unsupported value" )
153
+
154
+
155
+ def from_struct_value (value : google .protobuf .struct_pb2 .Value ) -> Any :
156
+ if value .HasField ("null_value" ):
157
+ return None
158
+ elif value .HasField ("bool_value" ):
159
+ return value .bool_value
160
+ elif value .HasField ("number_value" ):
161
+ return value .number_value
162
+ elif value .HasField ("string_value" ):
163
+ return value .string_value
164
+ elif value .HasField ("list_value" ):
165
+
166
+ return [from_struct_value (v ) for v in value .list_value .values ]
167
+ elif value .HasField ("struct_value" ):
168
+ return {k : from_struct_value (v ) for k , v in value .struct_value .fields .items ()}
169
+ else :
170
+ raise RuntimeError (f"invalid struct_pb2.Value: { value } " )
0 commit comments