@@ -1306,6 +1306,136 @@ def from_json(self: T, value: Union[str, bytes]) -> T:
13061306 """
13071307 return self .from_dict (json .loads (value ))
13081308
1309+ def to_pydict (
1310+ self , casing : Casing = Casing .CAMEL , include_default_values : bool = False
1311+ ) -> Dict [str , Any ]:
1312+ """
1313+ Returns a python dict representation of this object.
1314+
1315+ Parameters
1316+ -----------
1317+ casing: :class:`Casing`
1318+ The casing to use for key values. Default is :attr:`Casing.CAMEL` for
1319+ compatibility purposes.
1320+ include_default_values: :class:`bool`
1321+ If ``True`` will include the default values of fields. Default is ``False``.
1322+ E.g. an ``int32`` field will be included with a value of ``0`` if this is
1323+ set to ``True``, otherwise this would be ignored.
1324+
1325+ Returns
1326+ --------
1327+ Dict[:class:`str`, Any]
1328+ The python dict representation of this object.
1329+ """
1330+ output : Dict [str , Any ] = {}
1331+ defaults = self ._betterproto .default_gen
1332+ for field_name , meta in self ._betterproto .meta_by_field_name .items ():
1333+ field_is_repeated = defaults [field_name ] is list
1334+ value = getattr (self , field_name )
1335+ cased_name = casing (field_name ).rstrip ("_" ) # type: ignore
1336+ if meta .proto_type == TYPE_MESSAGE :
1337+ if isinstance (value , datetime ):
1338+ if (
1339+ value != DATETIME_ZERO
1340+ or include_default_values
1341+ or self ._include_default_value_for_oneof (
1342+ field_name = field_name , meta = meta
1343+ )
1344+ ):
1345+ output [cased_name ] = value
1346+ elif isinstance (value , timedelta ):
1347+ if (
1348+ value != timedelta (0 )
1349+ or include_default_values
1350+ or self ._include_default_value_for_oneof (
1351+ field_name = field_name , meta = meta
1352+ )
1353+ ):
1354+ output [cased_name ] = value
1355+ elif meta .wraps :
1356+ if value is not None or include_default_values :
1357+ output [cased_name ] = value
1358+ elif field_is_repeated :
1359+ # Convert each item.
1360+ value = [i .to_pydict (casing , include_default_values ) for i in value ]
1361+ if value or include_default_values :
1362+ output [cased_name ] = value
1363+ elif (
1364+ value ._serialized_on_wire
1365+ or include_default_values
1366+ or self ._include_default_value_for_oneof (
1367+ field_name = field_name , meta = meta
1368+ )
1369+ ):
1370+ output [cased_name ] = value .to_pydict (casing , include_default_values )
1371+ elif meta .proto_type == TYPE_MAP :
1372+ for k in value :
1373+ if hasattr (value [k ], "to_pydict" ):
1374+ value [k ] = value [k ].to_pydict (casing , include_default_values )
1375+
1376+ if value or include_default_values :
1377+ output [cased_name ] = value
1378+ elif (
1379+ value != self ._get_field_default (field_name )
1380+ or include_default_values
1381+ or self ._include_default_value_for_oneof (
1382+ field_name = field_name , meta = meta
1383+ )
1384+ ):
1385+ output [cased_name ] = value
1386+ return output
1387+
1388+ def from_pydict (self : T , value : Dict [str , Any ]) -> T :
1389+ """
1390+ Parse the key/value pairs into the current message instance. This returns the
1391+ instance itself and is therefore assignable and chainable.
1392+
1393+ Parameters
1394+ -----------
1395+ value: Dict[:class:`str`, Any]
1396+ The dictionary to parse from.
1397+
1398+ Returns
1399+ --------
1400+ :class:`Message`
1401+ The initialized message.
1402+ """
1403+ self ._serialized_on_wire = True
1404+ for key in value :
1405+ field_name = safe_snake_case (key )
1406+ meta = self ._betterproto .meta_by_field_name .get (field_name )
1407+ if not meta :
1408+ continue
1409+
1410+ if value [key ] is not None :
1411+ if meta .proto_type == TYPE_MESSAGE :
1412+ v = getattr (self , field_name )
1413+ if isinstance (v , list ):
1414+ cls = self ._betterproto .cls_by_field [field_name ]
1415+ for item in value [key ]:
1416+ v .append (cls ().from_pydict (item ))
1417+ elif isinstance (v , datetime ):
1418+ v = value [key ]
1419+ elif isinstance (v , timedelta ):
1420+ v = value [key ]
1421+ elif meta .wraps :
1422+ v = value [key ]
1423+ else :
1424+ # NOTE: `from_pydict` mutates the underlying message, so no
1425+ # assignment here is necessary.
1426+ v .from_pydict (value [key ])
1427+ elif meta .map_types and meta .map_types [1 ] == TYPE_MESSAGE :
1428+ v = getattr (self , field_name )
1429+ cls = self ._betterproto .cls_by_field [f"{ field_name } .value" ]
1430+ for k in value [key ]:
1431+ v [k ] = cls ().from_pydict (value [key ][k ])
1432+ else :
1433+ v = value [key ]
1434+
1435+ if v is not None :
1436+ setattr (self , field_name , v )
1437+ return self
1438+
13091439 def is_set (self , name : str ) -> bool :
13101440 """
13111441 Check if field with the given name has been set.
0 commit comments