0

I'm trying to do protocol decoding in Python.

Short version of my question:

  • I have received an array of bytes that is a packet of data.

    • Using the first byte of the packet, I know what type of packet it is.
    • Using that - I want to construct a packet via a class.
  • When i want to send a packet, really need to reuse about 90% of the processing done in the same packet class so that I can construct and send a packet.

I can do one of the above, I can't seem to do both

For example - something like this:


class PacketType123(BasePacketType):

    def __init__fromparams( self, field1, field2 ):
        # called when you want to send a packet
        # ... initialize like I'am sending the packet
        self.field1 = field1
        self.field2 = field2

    def encode_and_send(self,destination):
        # encode into raw byte
        databytes = self.encode()
        destination.send( databytes )

    # called when a packet is received on the wire
    def __init__frombytearray( self, bytearray ):
        self.decode_bytearray( bytearray )
        print("Hi Field2 = %d" % self.field2 )

# create packet decode lookup table.
lookuptable = dict()
# populate it with classes
lookuptable[123] = PacketType123
# other packets would added to the lookup table
lookuptable[44] = PacketType44
lookuptable[12] = PacketType12

To send

   p1 = PacketType1( 42, "dogs and cats" )
   p1.encode_and_send( server )

To process incomming data, do this:

   databytes = receive_message()

   packet = lookuptable[ datbytes[0] ].__init__frombytearray(databytes)

My problem is I can do either the ToSend or the ProcessIncomming, I don't know how to do both with the same class.

I've looked over:

It seems the only way to make this work is with keyword args.. Yuck that complicates other things for me... Looking for other way of doing this.

Suggestions please?

user3696153
  • 568
  • 5
  • 15
  • The reading and linked question certainly helps to show us what/how you have got to this point, but having additional examples from how you want the API to look like for your user would be even more useful. Then again, I am guessing you might not know exactly what you want. I am guessing that you have the `BasePacketType` class, your specific classes would inherit from that class, which will implement the specific constructors (i.e. the keyword arguments). Your question is how to turn a `bytearray` automagically into the correct subclass? – metatoaster Feb 16 '20 at 05:40
  • Essentially, I would imagine doing something like `AutoPacket(some_bytearray)`, if the `some_bytearray` is decoded into something that has been registered into the lookup table, it will then produce the correct type, e.g. it would produce `PacketType1` with `field1` set to `42` and `field2` set to `"dogs and cats"`. Let us know if this is closer to to how you imagine this API might appear. – metatoaster Feb 16 '20 at 05:43
  • Also it would be useful to explain why you can't do both in the same class, when you essentially almost have a working implementation - I think what you are missing is to declare both `decode_bytearray` and `__init__frombytearray` as `classmethod`, and that `decode_bytearray` need to return something rather than setting some attributes inside the implementation. It would be useful to include what your current implementation is for the line that invoked `self.decode_bytearray( bytearray )`. – metatoaster Feb 16 '20 at 06:06
  • metatoaster - You have exactly what I'm trying to do: – user3696153 Feb 17 '20 at 00:12
  • @metatoaster - There are two use cases, case (A) is I want to send a packet, so create that type of packet/class. The class/packet contains an array of fields, each field is a small dict - with a "field_name", "type" (u8, u16, etc), and "description" Some fields are a bit more complex (enumerations which are further decoded). In the send case, you populate the thing.field['name'].value = 123, and then later thing.encoded_and_send(), when the packet comes back the same array is used to decode the byte stream back into the field array. – user3696153 Feb 17 '20 at 00:17
  • @metatoast - I don't exactly get the idea of what a class method is. The other way to describe what I want is this: if I have a BaseMessage, how do I transform it upto a more complex class. For example, I receive a message: I can tell that it is a graphic object, and not an audio message. But in the end, would like that 'basic message' transformed into a graphic message, then transformed into for example a "triangle' or 'square' message. The problem is that there is no way to overload the __init__ function. – user3696153 Feb 17 '20 at 00:21
  • A `classmethod` is basically like an ordinary method, except it can be invoked directly without an instance of the class, and that the implicit `self` is replaced with the class itself - which allows the method to return not just anything, but also be allowed the access to the class to construct a new instance of the class (or any subclass automatically) and return. A better example would be the `setuptools` [`EntryPoint.parse`](https://github.com/pypa/setuptools/blob/v45.2.0/pkg_resources/__init__.py#L2480) classmethod, it returns an `EntryPoint` instance from a `str`. – metatoaster Feb 17 '20 at 00:52
  • To be even more explicit - `classmethod` is a very useful way to define alternative constructors for any given class in a way that can be propagated to any of their subclasses, without having to override the `__init__` method to accept alternative arguments - generally speaking, this applies if the constructors can generate those arguments, but there are ways to also side-step this using `__new__` (see one of the [linked answers](https://stackoverflow.com/a/38885481) in one of the threads you've linked). – metatoaster Feb 17 '20 at 00:57
  • One last thing: to reiterate, if you provide an example implementation of `decode_bytearray` I would be in a better position to provide an answer. – metatoaster Feb 17 '20 at 02:17

0 Answers0