Esempio n. 1
0
    def fields_in(kls, pkt, parent, serial):
        for name, typ in pkt.Meta.all_field_types:
            val = pkt.__getitem__(
                name,
                parent=parent,
                serial=serial,
                allow_bitarray=True,
                unpacking=False,
                do_transform=False,
            )
            size_bits = typ.size_bits
            if callable(size_bits):
                size_bits = size_bits(pkt)
            group = pkt.Meta.name_to_group.get(name, pkt.__class__.__name__)

            if not typ._multiple:
                yield FieldInfo(name, typ, val, size_bits, group)
            else:
                if not isinstance(val, list):
                    raise BadConversion("Expected field to be a list", name=name, val=type(val))

                number = typ._multiple
                if callable(number):
                    number = number(pkt)

                if len(val) != number:
                    raise BadConversion(
                        "Expected correct number of items", name=name, found=len(val), want=number
                    )

                for v in val:
                    yield FieldInfo(name, typ, v, size_bits, group)
Esempio n. 2
0
    def unpack_bytes(kls, data, protocol, pkt_type, protocol_register, unknown_ok=False):
        if isinstance(data, str):
            data = binascii.unhexlify(data)

        if isinstance(data, bytes):
            b = bitarray(endian="little")
            b.frombytes(data)
            data = b

        prot = protocol_register.get(protocol)
        if prot is None:
            raise BadConversion(
                "Unknown packet protocol", wanted=protocol, available=list(protocol_register)
            )
        Packet, messages_register = prot

        mkls = None
        for k in messages_register:
            if pkt_type in k.by_type:
                mkls = k.by_type[pkt_type]
                break

        if mkls is None:
            if unknown_ok:
                mkls = Packet
            else:
                raise BadConversion("Unknown message type!", protocol=protocol, pkt_type=pkt_type)

        return mkls.create(data)
Esempio n. 3
0
    def pack(self, em, meta, val):
        """Get us the value of the specified member of the enum"""
        available = []
        for name, member in em.__members__.items():
            available.append((name, member.value))
            if val == name or val == repr(
                    member) or val == member.value or val is member:
                return member.value

        if self.allow_unknown:
            if isinstance(val, int) and not isinstance(val, bool):
                return val
            elif isinstance(val, UnknownEnum):
                return val.value
            elif isinstance(val, str):
                m = regexes["unknown_enum"].match(val)
                if m:
                    return int(m["value"])

        if isinstance(val, enum.Enum):
            raise BadConversion("Can't convert value of wrong Enum",
                                val=val,
                                wanted=em,
                                got=type(val),
                                meta=meta)
        else:
            raise BadConversion("Value wasn't a valid enum value",
                                val=val,
                                available=available,
                                meta=meta)
Esempio n. 4
0
    def to_bitarray(self, meta, val):
        """Return us the val as a bitarray"""
        if type(val) is bitarray:
            return val

        b = bitarray(endian="little")
        if type(val) is str:
            # We care about when the single quotes aren't here for when we copy output from `lifx unpack` into a `lifx pack`
            # This is because we say something like `lifx pack -- '{"thing": "<class 'delfick_project.norms.spec_base.NotSpecified'>"}'
            # And the quotes cancel each other out
            if val in (
                    "<class delfick_project.norms.spec_base.NotSpecified>",
                    "<class 'delfick_project.norms.spec_base.NotSpecified'>",
            ):
                val = ""

            try:
                b.frombytes(binascii.unhexlify(val))
            except binascii.Error as error:
                raise BadConversion("Failed to turn str into bytes",
                                    meta=meta,
                                    val=val,
                                    error=error)
        else:
            try:
                b.frombytes(val)
            except TypeError:
                raise BadConversion("Failed to get a bitarray from the value",
                                    value=val,
                                    meta=meta)

        return b
Esempio n. 5
0
    def unpack(self, bitmask, meta, val):
        """Get us a list of bitmask members contained within the value"""
        result = []
        for v in val:
            if isinstance(v, bitmask):
                result.append(v)
            elif isinstance(v, enum.Enum):
                raise BadConversion(
                    "Can't convert value of wrong Enum",
                    val=v,
                    wanted=bitmask,
                    got=type(v),
                    meta=meta,
                )
            else:
                if type(v) is int:
                    for name, member in bitmask.__members__.items():
                        if type(v) is int and v & member.value:
                            result.append(member)
                else:
                    found = False
                    for name, member in bitmask.__members__.items():
                        if v == name or v == repr(member):
                            result.append(member)
                            found = True
                            break

                    if not found:
                        raise BadConversion(
                            "Can't convert value into value from mask",
                            val=v,
                            wanted=bitmask)

        return set(result)
Esempio n. 6
0
    def pack(self, bitmask, meta, val):
        """Return us a sum'd value of the bitmask members referred to by val"""
        final = 0
        used = []
        for v in val:
            if isinstance(v, bitmask):
                if v not in used:
                    final += v.value
                    used.append(v)
            elif isinstance(v, enum.Enum):
                raise BadConversion(
                    "Can't convert value of wrong Enum",
                    val=v,
                    wanted=bitmask,
                    got=type(v),
                    meta=meta,
                )
            else:
                found = False
                for name, member in bitmask.__members__.items():
                    if v == name or v == repr(member) or v == member.value:
                        if member not in used:
                            final += member.value
                            used.append(member)
                        found = True
                        break

                if not found:
                    raise BadConversion("Can't convert value into mask",
                                        mask=bitmask,
                                        got=v)

        return final
Esempio n. 7
0
    def to_bitarray(self):
        fmt = self.typ.struct_format
        val = self.value

        if val is sb.NotSpecified:
            raise BadConversion("Cannot pack an unspecified value",
                                got=val,
                                field=self.name,
                                group=self.group,
                                typ=self.typ)

        if type(val) is bitarray:
            return val

        if type(fmt) is str:
            return self.struct_format(fmt, val)

        elif fmt is bool:
            if type(val) is not bool:
                raise BadConversion(
                    "Trying to convert a non boolean into 1 bit",
                    got=val,
                    group=self.group,
                    field=self.name)
            return (bitarray("0", endian="little")
                    if val is False else bitarray("1", endian="little"))

        else:
            b = bitarray(endian="little")
            b.frombytes(val)
            return b
Esempio n. 8
0
    def unpack(self, em, meta, val):
        """Get us a member of the enum"""
        if isinstance(val, em):
            return val
        elif isinstance(val, enum.Enum):
            raise BadConversion("Can't convert value of wrong Enum",
                                val=val,
                                wanted=em,
                                got=type(val),
                                meta=meta)

        available = []
        for name, member in em.__members__.items():
            available.append((name, member.value))
            if val == name or val == repr(member) or val == member.value:
                return member

        if self.allow_unknown:
            if isinstance(val, int) and not isinstance(val, bool):
                return UnknownEnum(val)
            elif isinstance(val, UnknownEnum):
                return val
            elif isinstance(val, str):
                m = regexes["unknown_enum"].match(val)
                if m:
                    return UnknownEnum(int(m["value"]))

        # Only here if didn't match any members
        raise BadConversion(
            "Value is not a valid value of the enum",
            val=val,
            enum=em,
            available=available,
            meta=meta,
        )
Esempio n. 9
0
    def get_message_type(kls, data, protocol_register):
        """
        Given a ProtocolRegister and some data (bytes or dictionary)
        return ``(pkt_type, Packet, kls, pkt)``

        pkt_type
            The number assigned to this payload type. This code assumes the
            unpacked data has a ``pkt_type`` property that is retrieved for this.

        Packet
            The ``parent_packet`` class for the protocol the data represents.

            If ``data`` is bytes, get the integer from bits 16 to 28 as protocol.

            Use this number in protocol register to find the ``parent_packet``.

        kls
            The payload class representing this protocol and pkt_type.

        pkt
            An instance of the ``parent_packet`` from the data.
        """
        if type(data) is str:
            data = binascii.unhexlify(data)

        if type(data) is bytes:
            b = bitarray(endian="little")
            b.frombytes(data)
            data = b

        protocol = struct.unpack("<H", data[16:16 + 12].tobytes())[0]

        prot = protocol_register.get(protocol)
        if prot is None:
            raise BadConversion("Unknown packet protocol",
                                wanted=protocol,
                                available=list(protocol_register))
        Packet, messages_register = prot

        pkt = Packet.unpack(data)
        message_type = dictobj.__getitem__(pkt, "pkt_type")

        k = None
        for k in (messages_register or [kls]):
            if message_type in k.by_type:
                if pkt.payload is NotSpecified and k.by_type[
                        message_type].Payload.Meta.field_types:
                    raise BadConversion("packet had no payload", got=repr(pkt))
                break

        if k is None:
            return message_type, Packet, None, pkt
        return message_type, Packet, k.by_type.get(message_type), pkt
Esempio n. 10
0
    def packet_type_from_dict(kls, data):
        if "protocol" in data:
            protocol = data["protocol"]
        elif "frame_header" in data and "protocol" in data["frame_header"]:
            protocol = data["frame_header"]["protocol"]
        else:
            raise BadConversion("Couldn't work out protocol from dictionary", got=data)

        if "pkt_type" in data:
            pkt_type = data["pkt_type"]
        elif "pkt_type" in data.get("protocol_header", {}):
            pkt_type = data["protocol_header"]["pkt_type"]
        else:
            raise BadConversion("Couldn't work out pkt_type from dictionary", got=data)

        return protocol, pkt_type
Esempio n. 11
0
    def unpackd(self):
        val = self.val
        typ = self.typ
        fmt = typ.struct_format

        if fmt is None:
            return val.tobytes()

        if fmt is bool and self.size_bits is 1:
            return False if val.to01() is '0' else True

        if len(val) < typ.original_size:
            padding = bitarray('0' * (typ.original_size - len(val)),
                               endian="little")
            if getattr(self.typ, "left_cut", False):
                val = padding + val
            else:
                val = val + padding

        try:
            return struct.unpack(typ.struct_format, val.tobytes())[0]
        except (struct.error, TypeError, ValueError) as error:
            raise BadConversion("Failed to unpack field",
                                group=self.group,
                                field=self.name,
                                typ=typ,
                                val=val.to01(),
                                error=error)
Esempio n. 12
0
 def packet_type(kls, data):
     if isinstance(data, dict):
         return kls.packet_type_from_dict(data)
     elif isinstance(data, bytes):
         return kls.packet_type_from_bytes(data)
     elif isinstance(data, bitarray):
         return kls.packet_type_from_bitarray(data)
     else:
         raise BadConversion("Can't determine packet type from data", got=data)
Esempio n. 13
0
 def pack_payload(kls, pkt_type, data, messages_register=None):
     """
     Given some payload data as a dictionary and it's ``pkt_type``, return a
     hexlified string of the payload.
     """
     for k in messages_register or [kls]:
         if int(pkt_type) in k.by_type:
             return k.by_type[int(pkt_type)].Payload.create(data).pack()
     raise BadConversion("Unknown message type!", pkt_type=pkt_type)
Esempio n. 14
0
    def packet_type_from_bytes(kls, data):
        if len(data) < 4:
            raise BadConversion("Data is too small to be a LIFX packet", got=len(data))

        b = bitarray(endian="little")
        b.frombytes(data[2:4])
        protbts = b[:12].tobytes()
        protocol = protbts[0] + (protbts[1] << 8)

        pkt_type = None

        if protocol == 1024:
            if len(data) < 36:
                raise BadConversion(
                    "Data is too small to be a LIFX packet", need_atleast=36, got=len(data)
                )
            pkt_type = data[32] + (data[33] << 8)

        return protocol, pkt_type
Esempio n. 15
0
 def pack_payload(kls, message_type, data, messages_register=None):
     """
     Given some payload data as a dictionary and it's ``pkt_type``, return a
     hexlified string of the payload.
     """
     for k in (messages_register or [kls]):
         if int(message_type) in k.by_type:
             return k.by_type[int(message_type)].Payload.normalise(
                 Meta.empty(), data).pack()
     raise BadConversion("Unknown message type!", pkt_type=message_type)
Esempio n. 16
0
    def pack(self, em, meta, val):
        """Get us the value of the specified member of the enum"""
        available = []
        for name, member in em.__members__.items():
            available.append((name, member.value))
            if val == name or val == repr(
                    member) or val == member.value or val is member:
                return member.value

        if isinstance(val, enum.Enum):
            raise BadConversion("Can't convert value of wrong Enum",
                                val=val,
                                wanted=em,
                                got=type(val),
                                meta=meta)
        else:
            raise BadConversion("Value wasn't a valid enum value",
                                val=val,
                                available=available,
                                meta=meta)
Esempio n. 17
0
    def packet_type_from_bitarray(kls, data):
        if len(data) < 28:
            raise BadConversion("Data is too small to be a LIFX packet", got=len(data) // 8)

        b = bitarray(endian="little")
        b.extend(data[16 : 16 + 12])
        protbts = b.tobytes()
        protocol = protbts[0] + (protbts[1] << 8)

        pkt_type = None

        if protocol == 1024:
            if len(data) < 288:
                raise BadConversion(
                    "Data is too small to be a LIFX packet", need_atleast=36, got=len(data) // 8
                )

            ptbts = data[256 : 256 + 16].tobytes()
            pkt_type = ptbts[0] + (ptbts[1] << 8)

        return protocol, pkt_type
Esempio n. 18
0
    def pack(kls, data, protocol_register, unknown_ok=False):
        """
        Return a hexlified string of the data.

        This uses ``pkt_type`` and ``protocol`` in the data, along with the
        protocol_register to find the appropriate class to use to perform the
        packing.
        """
        if "pkt_type" in data:
            message_type = data["pkt_type"]
        elif "pkt_type" in data.get("protocol_header", {}):
            message_type = data["protocol_header"]["pkt_type"]
        else:
            raise BadConversion(
                "Don't know how to pack this dictionary, it doesn't specify a pkt_type!"
            )

        if "protocol" in data:
            protocol = data["protocol"]
        elif "frame_header" in data and "protocol" in data["frame_header"]:
            protocol = data["frame_header"]["protocol"]
        else:
            raise BadConversion(
                "Don't know how to pack this dictionary, it doesn't specify a protocol!"
            )

        prot = protocol_register.get(protocol)
        if prot is None:
            raise BadConversion("Unknown packet protocol",
                                wanted=protocol,
                                available=list(protocol_register))
        Packet, messages_register = prot

        for k in (messages_register or [kls]):
            if message_type in k.by_type:
                return k.by_type[message_type].normalise(Meta.empty(),
                                                         data).pack()
        if unknown_ok:
            return Packet.normalise(Meta.empty(), data).pack()
        raise BadConversion("Unknown message type!", pkt_type=message_type)
Esempio n. 19
0
 def struct_format(self, fmt, val):
     b = bitarray(endian="little")
     try:
         if val is Optional:
             val = 0
         b.frombytes(struct.pack(fmt, val))
     except struct.error as error:
         raise BadConversion("Failed trying to convert a value",
                             val=val,
                             fmt=fmt,
                             error=error,
                             group=self.group,
                             name=self.name)
     return b
Esempio n. 20
0
    def unpack(self, em, meta, val):
        """Get us a member of the enum"""
        if isinstance(val, em):
            return val
        elif isinstance(val, enum.Enum):
            raise BadConversion("Can't convert value of wrong Enum",
                                val=val,
                                wanted=em,
                                got=type(val),
                                meta=meta)

        available = []
        for name, member in em.__members__.items():
            available.append((name, member.value))
            if val == name or val == repr(member) or val == member.value:
                return member

        # Only here if didn't match any members
        raise BadConversion("Value is not a valid value of the enum",
                            val=val,
                            enum=em,
                            available=available,
                            meta=meta)
Esempio n. 21
0
    def unpack(kls, data, protocol_register, unknown_ok=False):
        """
        Return a fully resolved packet instance from the data and protocol_register.

        unknown_ok
            Whether we return an instance of the parent_packet with unresolved
            payload if we don't have a payload class, or if we raise an error
        """
        message_type, Packet, PacketKls, pkt = kls.get_message_type(
            data, protocol_register)
        if PacketKls:
            return kls.unpack_pkt(PacketKls, pkt)
        elif unknown_ok:
            return pkt
        raise BadConversion("Unknown message type!", pkt_type=message_type)
Esempio n. 22
0
    def get_packet_type(kls, data, protocol_register):
        """
        Given a ProtocolRegister and some data (bytes or dictionary)
        return ``(protocol, pkt_type, Packet, kls, data)``

        protocol
            The number specifying the "protocol" of this message. This is
            likely to always be 1024.

        pkt_type
            The number assigned to this payload type. This code assumes the
            unpacked data has a ``pkt_type`` property that is retrieved for this.

        Packet
            The ``parent_packet`` class for the protocol the data represents.

            If ``data`` is bytes, get the integer from bits 16 to 28 as protocol.

            Use this number in protocol register to find the ``parent_packet``.

        kls
            The payload class representing this protocol and pkt_type.

            This is None if the protocol/pkt_type pair is unknown

        data
            If the data was a dictionary, bytes or bitarray then returned as is
            if the data was a str, then we return it as unhexlified bytes
        """
        if isinstance(data, str):
            data = binascii.unhexlify(data)

        protocol, pkt_type = PacketTypeExtractor.packet_type(data)

        prot = protocol_register.get(protocol)
        if prot is None:
            raise BadConversion(
                "Unknown packet protocol", wanted=protocol, available=list(protocol_register)
            )
        Packet, messages_register = prot

        mkls = None
        for k in messages_register:
            if pkt_type in k.by_type:
                mkls = k.by_type[pkt_type]
                break

        return protocol, pkt_type, Packet, mkls, data
Esempio n. 23
0
def val_to_bitarray(val, doing):
    """Convert a value into a bitarray"""
    if type(val) is bitarray:
        return val

    if type(val) is str:
        val = binascii.unhexlify(val.encode())

    if type(val) is not bytes:
        raise BadConversion("Couldn't get bitarray from a value",
                            value=val,
                            doing=doing)

    b = bitarray(endian="little")
    b.frombytes(val)
    return b
Esempio n. 24
0
    def pack(kls, data, protocol_register, unknown_ok=False):
        """
        Return a hexlified string of the data.

        This uses ``pkt_type`` and ``protocol`` in the data, along with the
        protocol_register to find the appropriate class to use to perform the
        packing.
        """
        protocol, pkt_type, Packet, PacketKls, data = kls.get_packet_type(data, protocol_register)

        if PacketKls is None:
            if not unknown_ok:
                raise BadConversion("Unknown message type!", protocol=protocol, pkt_type=pkt_type)
            PacketKls = Packet

        return PacketKls.create(data).pack()
Esempio n. 25
0
    def create(kls, data, protocol_register, unknown_ok=False):
        """
        Return a fully resolved packet instance from the data and protocol_register.

        unknown_ok
            Whether we return an instance of the parent_packet with unresolved
            payload if we don't have a payload class, or if we raise an error
        """
        protocol, pkt_type, Packet, PacketKls, data = kls.get_packet_type(data, protocol_register)

        if PacketKls:
            return PacketKls.create(data)

        if unknown_ok:
            return Packet.create(data)

        raise BadConversion("Unknown message type!", protocol=protocol, pkt_type=pkt_type)
Esempio n. 26
0
    def _spec(self, pkt, unpacking=False):
        """
        Return a delfick_project.norms spec object for normalising values before and
        after packing/unpacking
        """
        spec = self.spec_from_conversion(pkt, unpacking)

        if spec is not None:
            if self._dynamic is sb.NotSpecified:
                return spec
            else:
                return self.dynamic_wrapper(spec, pkt, unpacking=unpacking)

        raise BadConversion(
            "Cannot create a specification for this conversion",
            conversion=self.conversion,
            type=self.__class__.__name__,
        )
Esempio n. 27
0
    def pack(kls, pkt, payload=None, parent=None, serial=None):
        """
        This uses the ``Meta`` on the packet to determine the order of all the
        fields and uses that to extract values from the object and uses the type
        object for each field to convert the value into a bitarray object.

        Finally, the bitarray object is essentially concated together to create
        one final bitarray object.

        This code assumes the packet has little endian.

        If ``payload`` is provided and this packet is a ``parent_packet`` and
        it's last field has a ``message_type`` property of 0, then that payload
        is converted into a bitarray and added to the end of the result.
        """
        final = bitarray(endian="little")

        for info in kls.fields_in(pkt, parent, serial):
            result = info.to_sized_bitarray()

            if result is None:
                raise BadConversion("Failed to convert field into a bitarray",
                                    field=info.as_dict())

            final += result

        # If this is a parent packet with a Payload of message_type 0
        # Then this means we have no payload fields and so must append
        # The entire payload at the end
        # As opposed to individual fields in the payload one at a time
        if getattr(pkt, "parent_packet", False) and pkt.Meta.field_types:
            name, typ = pkt.Meta.field_types[-1]
            if getattr(typ, "message_type", None) is 0:
                final += val_to_bitarray(
                    payload or pkt[name],
                    doing="Adding payload when packing a packet")

        return final