def stream_one_item(self, f_type: Type, item, f: BinaryIO) -> None: inner_type: Type if is_type_List(f_type): assert is_type_List(type(item)) f.write(uint32(len(item)).to_bytes(4, "big")) inner_type = f_type.__args__[0] assert inner_type != List.__args__[0] # type: ignore for element in item: self.stream_one_item(inner_type, element, f) elif is_type_SpecificOptional(f_type): inner_type = f_type.__args__[0] if item is None: f.write(bytes([0])) else: f.write(bytes([1])) self.stream_one_item(inner_type, item, f) elif hasattr(f_type, "stream"): item.stream(f) elif hasattr(f_type, "__bytes__"): f.write(bytes(item)) elif f_type is str: f.write(uint32(len(item)).to_bytes(4, "big")) f.write(item.encode("utf-8")) else: raise NotImplementedError(f"can't stream {item}, {f_type}")
def stream_one_item(self, f_type: Type, item, f: BinaryIO) -> None: inner_type: Type if is_type_List(f_type): assert is_type_List(type(item)) f.write(uint32(len(item)).to_bytes(4, "big")) 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_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 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 == bytes: f.write(uint32(len(item)).to_bytes(4, "big")) f.write(item) elif hasattr(f_type, "stream"): item.stream(f) elif hasattr(f_type, "__bytes__"): f.write(bytes(item)) elif f_type is str: f.write(uint32(len(item)).to_bytes(4, "big")) f.write(item.encode("utf-8")) elif f_type is bool: f.write(int(item).to_bytes(4, "big")) else: raise NotImplementedError(f"can't stream {item}, {f_type}")
def parse_one_item(cls: Type[cls.__name__], f_type: Type, f: BinaryIO): # type: ignore inner_type: Type if is_type_List(f_type): inner_type = f_type.__args__[0] full_list: List[inner_type] = [] # type: ignore assert inner_type != List.__args__[0] # type: ignore list_size: uint32 = uint32(int.from_bytes(f.read(4), "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_SpecificOptional(f_type): inner_type = f_type.__args__[0] is_present: bool = f.read(1) == bytes([1]) if is_present: return cls.parse_one_item(inner_type, f) # type: ignore else: return None if hasattr(f_type, "parse"): return f_type.parse(f) if hasattr(f_type, "from_bytes") and size_hints[f_type.__name__]: return f_type.from_bytes(f.read(size_hints[f_type.__name__])) if f_type is str: str_size: uint32 = uint32(int.from_bytes(f.read(4), "big")) return bytes.decode(f.read(str_size), "utf-8") else: raise RuntimeError(f"Type {f_type} does not have parse")
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): return tuple(dataclass_from_dict(get_args(klass)[0], item) for item in d) 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 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)
def parse_one_item(cls: Type[cls.__name__], f_type: Type, f: BinaryIO): # type: ignore inner_type: Type 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 = 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_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 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 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 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(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 hasattr(f_type, "parse"): return f_type.parse(f) if hasattr(f_type, "from_bytes") and size_hints[f_type.__name__]: 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") else: raise RuntimeError(f"Type {f_type} does not have parse")
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)
def test_not_lists(self): assert not is_type_List(Dict)