Exemplo n.º 1
0
    def pkt_from_bitarray(kls, pkt_kls, value):
        i = 0
        final = pkt_kls()

        for name, typ in pkt_kls.Meta.all_field_types:
            single_size_bits = typ.size_bits
            if callable(single_size_bits):
                single_size_bits = single_size_bits(final)

            multiple = typ._multiple

            size_bits = single_size_bits
            if multiple:
                if callable(multiple):
                    multiple = multiple(final)
                size_bits *= multiple

            val = value[i : i + size_bits]
            i += size_bits

            if multiple:
                if typ.struct_format:
                    res = []
                    j = 0
                    for _ in range(multiple):
                        v = val[j : j + single_size_bits]
                        j += single_size_bits
                        info = BitarraySlice(name, typ, v, single_size_bits, pkt_kls.__name__)
                        res.append(info.unpackd)
                    val = res
                final[name] = val
            else:
                info = BitarraySlice(name, typ, val, size_bits, pkt_kls.__name__)
                dictobj.__setitem__(final, info.name, info.unpackd)
        return final, i
Exemplo n.º 2
0
    def _set_group_item(self, key, val):
        """
        Used by __setitem__ to put a group field onto the packet

        Ensures:
        * Empty payload group is only filled by str/bytes/bitarray
        * sb.NotSpecified translates to setting that for all the fields in the group
        * When val is an instance of the type for this group, we transfer values directly
        * Non empty payload groups is filled by something with items()
          so we can then set the values for the fields in the group
        """
        typ = self.Meta.field_types_dict.get(key)
        if getattr(typ, "message_type", None) == 0:
            # Message_type of 0 indicates an empty Payload
            # So we store the whole thing as is on the packet
            # We also make sure this is a str (hexlified bytes), bytes or bitarray
            # __getitem__ has the opposite logic to grab this group as is
            if type(val) not in (bytes, bitarray, str):
                msg = "Setting non bytes payload on a packet that doesn't know what fields it's payload has"
                raise ValueError("{0}\tkey={1}\tgot={2}".format(
                    msg, key, repr(val)))

            dictobj.__setitem__(self, key, val)
            return

        if val is sb.NotSpecified:
            for field in self.Meta.groups[key]:
                if field in self:
                    self[field] = sb.NotSpecified
            return

        # if we're setting a group from an instance of the group
        # then just steal the raw values without the transform dance
        if hasattr(typ, "Meta") and issubclass(type(val), typ):
            field_types = typ.Meta.field_types_dict
            for field, v in val.actual_items():
                if field in field_types:
                    dictobj.__setitem__(self, field, v)
            return

        # We're setting a group, we need to get things from the group via items
        if not hasattr(val, "items"):
            msg = "Setting a group on a packet must be done with a value that has an items() method"
            raise ValueError("{0}\tkey={1}\tgot={2}".format(
                msg, key, repr(val)))

        # Set from our value
        for field, v in val.items():
            if field in self.Meta.groups[key]:
                self[field] = v
Exemplo n.º 3
0
    def clone(self, overrides=None):
        """
        Efficiently create a shallow copy of this packet without going
        through delfick_project.norms

        The exception is anything in overrides will go through delfick_project.norms
        """
        clone = self.__class__()

        for key, value in self.actual_items():
            if overrides and key in overrides:
                clone[key] = overrides[key]
            else:
                dictobj.__setitem__(clone, key, value)

        return clone
Exemplo n.º 4
0
    def normalise(self, meta, val):
        val = sb.dictionary_spec().normalise(meta, val)

        pkt = self.kls()
        for attr, spec in self.attrs:
            if callable(spec):
                spec = spec(pkt, False)

            v = val.get(attr, pkt.actual(attr))
            if attr not in val and attr in self.name_to_group:
                g = self.name_to_group[attr]
                if g in val and attr in val[g]:
                    v = val[g][attr]

            v = spec.normalise(meta.at(attr), v)
            dictobj.__setitem__(pkt, attr, v)

        return pkt
Exemplo n.º 5
0
    def __setitem__(self, key, val):
        """
        Set values on the object

        This will unpack dictionaries if we are setting a dictionary for a group
        field.

        We also see if a field has a transform option and use it if it's there
        """
        if key in self.Meta.groups:
            if val is Initial:
                # Special case because of the logic in dictobj that sets default values on initialization
                # Should only happen for group fields where it makes no sense
                return

            self._set_group_item(key, val)
            return

        # If our type has a transformation, apply it to our value
        # The assumption is that if there is a transformation,
        # The value will always be given with the transformation in mind
        # untransform will be used when extracting the value
        typ = self.Meta.all_field_types_dict.get(key)
        if typ and typ._transform is not sb.NotSpecified and val not in (
                sb.NotSpecified, Optional):
            val = typ.do_transform(self, val)

        # If we have multiple, then we want to make sure we create actual objects
        # So that if we modify an object in place, it's updated on the packet
        if typ and hasattr(
                typ,
                "_multiple") and typ._multiple and val is not sb.NotSpecified:
            val = typ.spec(self,
                           unpacking=True).normalise(Meta.empty().at(key), val)

        # Otherwise we set directly on the packet
        dictobj.__setitem__(self, key, val)
Exemplo n.º 6
0
    def __getitem__(
        self,
        key,
        do_spec=True,
        do_transform=True,
        parent=None,
        serial=None,
        allow_bitarray=False,
        unpacking=True,
    ):
        """
        Dictionary access for a key on the object

        This works for groups as well where the values in the fields for that
        group are returned as a dictionary.

        This process will use ``delfick_project.norms`` to ensure the value you get
        back is normalised. Unless ``do_spec`` is ``False``.

        We will also use the type transform unless ``do_transform`` is ``False``.

        And finally if the do_spec is True and we get back a bitarray and allow_bitarray is False,
        we will return the result of calling tobytes on that object.
        """
        M = object.__getattribute__(self, "Meta")

        # If we're requesting one of the groups, then we must assemble from the keys in that group
        # Unless the group is an empty Payload (message_type of 0 on the type)
        # In that case, there are no fields, so we return as it is found on the class
        # (__setitem__ has logic to store this group on the class as is)
        if key in M.groups:
            field_types = M.field_types_dict
            if key in field_types and getattr(field_types[key], "message_type",
                                              None) != 0:
                final = field_types[key]()
                for k in M.groups[key]:
                    actual = self.actual(k)
                    if actual is sb.NotSpecified:
                        final[k] = self.__getitem__(k,
                                                    parent=self,
                                                    serial=serial)
                    else:
                        dictobj.__setitem__(final, k, self.actual(k))
                return final

        try:
            actual = super(dictobj, self).__getitem__(key)
        except KeyError:
            if key not in M.all_names and key not in M.groups:
                raise
            actual = sb.NotSpecified

        if key in M.groups and actual is not sb.NotSpecified:
            # Can only mean we have an empty payload group here
            b = val_to_bitarray(actual,
                                doing="Converting payload from parent packet")
            if not allow_bitarray:
                return b.tobytes()
            else:
                return b

        if do_spec and key in M.all_names:
            typ = M.all_field_types_dict[key]
            res = object.__getattribute__(self,
                                          "getitem_spec")(typ, key, actual,
                                                          parent, serial,
                                                          do_transform,
                                                          allow_bitarray,
                                                          unpacking)

            # Make it so if there isn't a list specified, but you access it, we store the list we return
            # So that if you modify that list, it modifies on the packet
            if typ and hasattr(
                    typ, "_multiple"
            ) and typ._multiple and actual is sb.NotSpecified:
                dictobj.__setitem__(self, key, res)

            return res

        return actual