Ejemplo n.º 1
0
def dataclass_from_dict(klass, d):
    """
    Converts a dictionary based on a dataclass, into an instance of that dataclass.
    Recursively goes through lists, optionals, and dictionaries.
    """
    if is_type_SpecificOptional(klass):
        # Type is optional, data is either None, or Any
        if not d:
            return None
        return dataclass_from_dict(get_args(klass)[0], d)
    elif is_type_Tuple(klass):
        # Type is tuple, can have multiple different types inside
        i = 0
        klass_properties = []
        for item in d:
            klass_properties.append(dataclass_from_dict(klass.__args__[i], item))
            i = i + 1
        return tuple(klass_properties)
    elif dataclasses.is_dataclass(klass):
        # Type is a dataclass, data is a dictionary
        fieldtypes = {f.name: f.type for f in dataclasses.fields(klass)}
        return klass(**{f: dataclass_from_dict(fieldtypes[f], d[f]) for f in d})
    elif is_type_List(klass):
        # Type is a list, data is a list
        return [dataclass_from_dict(get_args(klass)[0], item) for item in d]
    elif issubclass(klass, bytes):
        # Type is bytes, data is a hex string
        return klass(hexstr_to_bytes(d))
    elif klass.__name__ in unhashable_types:
        # Type is unhashable (bls type), so cast from hex string
        return klass.from_bytes(hexstr_to_bytes(d))
    else:
        # Type is a primitive, cast with correct class
        return klass(d)
Ejemplo n.º 2
0
 def function_to_parse_one_item(cls: Type[cls.__name__], f_type: Type):  # type: ignore
     """
     This function returns a function taking one argument `f: BinaryIO` that parses
     and returns a value of the given type.
     """
     inner_type: Type
     if f_type is bool:
         return parse_bool
     if is_type_SpecificOptional(f_type):
         inner_type = get_args(f_type)[0]
         parse_inner_type_f = cls.function_to_parse_one_item(inner_type)
         return lambda f: parse_optional(f, parse_inner_type_f)
     if hasattr(f_type, "parse"):
         return f_type.parse
     if f_type == bytes:
         return parse_bytes
     if is_type_List(f_type):
         inner_type = get_args(f_type)[0]
         parse_inner_type_f = cls.function_to_parse_one_item(inner_type)
         return lambda f: parse_list(f, parse_inner_type_f)
     if is_type_Tuple(f_type):
         inner_types = get_args(f_type)
         list_parse_inner_type_f = [cls.function_to_parse_one_item(_) for _ in inner_types]
         return lambda f: parse_tuple(f, list_parse_inner_type_f)
     if hasattr(f_type, "from_bytes") and f_type.__name__ in size_hints:
         bytes_to_read = size_hints[f_type.__name__]
         return lambda f: parse_size_hints(f, f_type, bytes_to_read)
     if f_type is str:
         return parse_str
     raise NotImplementedError(f"Type {f_type} does not have parse")
Ejemplo n.º 3
0
 def parse_one_item(cls: Type[cls.__name__], f_type: Type, f: BinaryIO):  # type: ignore
     inner_type: Type
     if f_type is bool:
         bool_byte = f.read(1)
         assert bool_byte is not None and len(bool_byte) == 1  # Checks for EOF
         if bool_byte == bytes([0]):
             return False
         elif bool_byte == bytes([1]):
             return True
         else:
             raise ValueError("Bool byte must be 0 or 1")
     if is_type_SpecificOptional(f_type):
         inner_type = get_args(f_type)[0]
         is_present_bytes = f.read(1)
         assert is_present_bytes is not None and len(is_present_bytes) == 1  # Checks for EOF
         if is_present_bytes == bytes([0]):
             return None
         elif is_present_bytes == bytes([1]):
             return cls.parse_one_item(inner_type, f)  # type: ignore
         else:
             raise ValueError("Optional must be 0 or 1")
     if hasattr(f_type, "parse"):
         return f_type.parse(f)
     if f_type == bytes:
         list_size_bytes = f.read(4)
         assert list_size_bytes is not None and len(list_size_bytes) == 4  # Checks for EOF
         list_size: uint32 = uint32(int.from_bytes(list_size_bytes, "big"))
         bytes_read = f.read(list_size)
         assert bytes_read is not None and len(bytes_read) == list_size
         return bytes_read
     if is_type_List(f_type):
         inner_type = get_args(f_type)[0]
         full_list: List[inner_type] = []  # type: ignore
         # wjb assert inner_type != get_args(List)[0]  # type: ignore
         list_size_bytes = f.read(4)
         assert list_size_bytes is not None and len(list_size_bytes) == 4  # Checks for EOF
         list_size = uint32(int.from_bytes(list_size_bytes, "big"))
         for list_index in range(list_size):
             full_list.append(cls.parse_one_item(inner_type, f))  # type: ignore
         return full_list
     if is_type_Tuple(f_type):
         inner_types = get_args(f_type)
         full_list = []
         for inner_type in inner_types:
             full_list.append(cls.parse_one_item(inner_type, f))  # type: ignore
         return tuple(full_list)
     if hasattr(f_type, "from_bytes") and f_type.__name__ in size_hints:
         bytes_to_read = size_hints[f_type.__name__]
         bytes_read = f.read(bytes_to_read)
         assert bytes_read is not None and len(bytes_read) == bytes_to_read
         return f_type.from_bytes(bytes_read)
     if f_type is str:
         str_size_bytes = f.read(4)
         assert str_size_bytes is not None and len(str_size_bytes) == 4  # Checks for EOF
         str_size: uint32 = uint32(int.from_bytes(str_size_bytes, "big"))
         str_read_bytes = f.read(str_size)
         assert str_read_bytes is not None and len(str_read_bytes) == str_size  # Checks for EOF
         return bytes.decode(str_read_bytes, "utf-8")
     raise RuntimeError(f"Type {f_type} does not have parse")
Ejemplo n.º 4
0
 def test_basic_list(self):
     a = [1, 2, 3]
     assert is_type_List(type(a))
     assert is_type_List(List)
     assert is_type_List(List[int])
     assert is_type_List(List[uint8])
     assert is_type_List(list)
     assert not is_type_List(Tuple)  # type: ignore
     assert not is_type_List(tuple)
     assert not is_type_List(dict)
Ejemplo n.º 5
0
    def stream_one_item(self, f_type: Type, item, f: BinaryIO) -> None:
        inner_type: Type
        if is_type_SpecificOptional(f_type):
            inner_type = get_args(f_type)[0]
            if item is None:
                f.write(bytes([0]))
            else:
                f.write(bytes([1]))
                self.stream_one_item(inner_type, item, f)
        elif f_type == bytes:
            write_uint32(f, uint32(len(item)))
            f.write(item)
        elif hasattr(f_type, "stream"):
            item.stream(f)
        elif hasattr(f_type, "__bytes__"):
            f.write(bytes(item))
        elif is_type_List(f_type):
            assert is_type_List(type(item))
            write_uint32(f, uint32(len(item)))
            inner_type = get_args(f_type)[0]
            # wjb assert inner_type != get_args(List)[0]  # type: ignore
            for element in item:
                self.stream_one_item(inner_type, element, f)
        elif is_type_Tuple(f_type):
            inner_types = get_args(f_type)
            assert len(item) == len(inner_types)
            for i in range(len(item)):
                self.stream_one_item(inner_types[i], item[i], f)

        elif f_type is str:
            str_bytes = item.encode("utf-8")
            write_uint32(f, uint32(len(str_bytes)))
            f.write(str_bytes)
        elif f_type is bool:
            f.write(int(item).to_bytes(1, "big"))
        else:
            raise NotImplementedError(f"can't stream {item}, {f_type}")
Ejemplo n.º 6
0
 def test_not_lists(self):
     assert not is_type_List(Dict)