Google Types - Any support
Currently, betterproto does not understand Any messages, except that they have a url and bytes.
If you have suggestions for extended support, please be welcome to make them here.
@evgenus - You requested support for Any
Thanks for this PR. I trapped into that issue a hour ago. Please add also
import "google/protobuf/any.proto";Originally posted by @Evgenus in https://github.com/danielgtaylor/python-betterproto/pull/76#issuecomment-635306340
Since I have no experience with Any, will simply importing it be enough? Or does it need special behavior when parsing an Any message?
We could generate the Any object with url and bytes or fully decode it on the fly. Any thoughts on this?
@boukeversteegh I saw you are involve in the some issues related to the support of Any type.
Do you know if there's some news for supporting Any in the coming releases ?
I'm not actively participating in betterproto anymore, but seeing that no new comments were added here, it looks like there are no concrete plans for implementing it.
The first step would be to define what Any support would look like. You're very welcome to share what could be a useful form of support for your use case.
This is what I'm using right now. it doesn't play great with mypy (complains that it's not the google.lib.protobuf.Any) but otherwise it works for my JSON usecase:
@dataclass(eq=False, repr=False)
class Any(betterproto.Message):
type_url: str = betterproto.string_field(1)
value: betterproto.Message = betterproto.message_field(2)
def to_dict(
self, casing: betterproto.Casing = betterproto.Casing.CAMEL, include_default_values: bool = False
) -> Dict[str, Any]:
raw_dict = super().to_dict(casing, include_default_values)
dict_: Dict[str, Any] = {}
type_url = casing('type_url').rstrip("_") # type: ignore
if type_url in raw_dict:
dict_['@type'] = raw_dict[type_url]
value = casing('value').rstrip("_") # type: ignore
dict_.update(raw_dict.get(value, {}))
return dict_
Still needed. I am using it to interface with Envoy, which uses Any.
Modified above code to work with python 3.10:
def to_dict(
self, casing: betterproto.Casing = betterproto.Casing.SNAKE, include_default_values: bool = False
) -> Dict[str, object]:
raw_dict = super().to_dict(casing, include_default_values)
dict_: Dict[str, object] = {}
type_url = casing('type_url').rstrip("_") # type: ignore
if type_url in raw_dict:
dict_['@type'] = raw_dict[type_url]
value = casing('value').rstrip("_") # type: ignore
dict_.update(raw_dict.get(value, {}))
return dict_
here is my usecase :
I have the following proto message answer :
message QueryAccountsResponse {
// accounts are the existing accounts
repeated google.protobuf.Any accounts = 1 [(cosmos_proto.accepts_interface) = "cosmos.auth.v1beta1.AccountI"];
// pagination defines the pagination in the response.
cosmos.base.query.v1beta1.PageResponse pagination = 2;
}
betterproto generate the folowing class:
@dataclass(eq=False, repr=False)
class QueryAccountResponse(betterproto.Message):
"""
QueryAccountResponse is the response type for the Query/Account RPC method.
"""
account: "betterproto_lib_google_protobuf.Any" = betterproto.message_field(1)
"""account defines the account of the corresponding address."""
When I request from a REST API, I'm getting the following answer :
{'account':
{
'@type': '/cosmos.auth.v1beta1.BaseAccount',
'account_number': '38',
'address': 'c4e1s3z7kvhtx8u7uruflz208phy5tuzhzlgv0h50k',
'pub_key': None,
'sequence': '0'
}
}
I'm loading my Message with the following line :
QueryAccountResponse().from_dict(json.loads(json_response))
and get an empty object as result.
How can I have the ANY object ?
@sunds I am also looking into using this for an Envoy interface, but I do not have much experience using betterproto. Would it be possible for you to share a short example, how you use the modified Any class in your code, please? Thank you
[Update]: I think I figured it out. I included the wrong "Any" class