Options
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report Inappropriate Content
04-03-2022 10:41 PM
Hi,
The ProtoFetcher is just a wrapper for the pb2 file(s) created using the protoc. As of now, I am using Scala to do the same trick (it was about 5x faster).
To avoid the pickling problems, you need to build the ProtoFetcher library into a wheel file on a non-Databricks cluster (e.g. your laptop.) I personally prefer Python Poetry for managing libraries and the build process. Then upload this wheel to S3 and use is as a cluster library in Databricks.
The class itself is simply a wrapper for pb2 files:
from google.protobuf.pyext.cpp_message import GeneratedProtocolMessageType
from google.protobuf.json_format import MessageToJson
from ProtoFetcher.PROTO_OUT import example_message_pb2
# Hard-coded list of implemented messages. A single proto can contain many useful messages.
known_messages = {
"example_message": {
"proto_source": "example_message.proto",
"class": example_message_pb2,
"proto_messagename": "ExampleMessageList",
"proto_sub_messagename": "ExampleMessageValues",
},
}
class ProtoFetcher:
"""A class to represent a proto message. A wrapper for google.protobuf.
Parameters
----------
messagename : str
the common name of the service.
Examples
--------
>>> service = ProtoFetcher('example_message')
>>> serde = service.get_deserializer()
>>> 'some_field_that_is_part_of_the_proto_message' in serde.DESCRIPTOR.fields_by_name.keys()
True
"""
def __init__(self, messagename: str):
"""Constructs the proto message wrapper."""
# Input check
if messagename not in known_messages.keys():
raise NameError(f"The service must be one of: {self.list_message_names()}")
# Settings/Info for this messagename from the config
self.msg_info = known_messages[messagename]
# Initialize the message
self.deserializer = self._init_deserializer_class()
def _init_deserializer_class(self) -> GeneratedProtocolMessageType:
"""Gets the protocol message type from the Protobuf descriptors (_pb2.py file).
It can be used for serializing and deserializing the messages.
"""
# Get
message = getattr(self.msg_info["class"], self.msg_info["proto_messagename"])
# Some messages include a submessage. This happens at least when
# a blob field description is a list of lists.
if self.msg_info["proto_sub_messagename"]:
message = getattr(message, self.msg_info["proto_sub_messagename"])
return message
def get_deserializer(self) -> GeneratedProtocolMessageType:
"""Getter for the message that can be used for serializing and deserializing the messages."""
return self.deserializer
def blob_to_json(self, blob) -> str:
"""Converts the bytes-like BLOB object to a the human-readable form.
Parameters
----------
blob : str/bytes
bytes-like object or ASCII string including the contents of the blob field.
Returns
-------
j : str
Stringified JSON containing the deserialized message data.
"""
# Deserialize the proto
deserializer = self.get_deserializer()
message = deserializer.FromString(blob)
# Convert to JSON and clean it
my_message_as_json = MessageToJson(message)
j = json.dumps(json.loads(my_message_as_json))
return j