class DataReader: data = attrib() def __attrs_post_init__(self): if not hasattr(self.data, 'read'): self.data = BytesIO(self.data) def close(self): self.data.close() def peek(self, size=DEFAULT_BUFFER_SIZE): if size > DEFAULT_BUFFER_SIZE: size = DEFAULT_BUFFER_SIZE peeked = None try: peeked = self.data.peek(size)[:size] except AttributeError: pass if peeked is None or len(peeked) != size: peeked = self.data.read(size) self.data.seek(-len(peeked), os.SEEK_CUR) return peeked def read(self, size=None): return self.data.read(size) def seek(self, offset, whence=os.SEEK_SET): self.data.seek(offset, whence) def tell(self): return self.data.tell()
def bdecode(f_or_data): """ bdecodes data by looking up the type byte, and using it to look up the respective decoding function, which in turn is used to return the decoded object The parameter can be a file opened in bytes mode, bytes or a string (the last of which will be decoded) """ if isinstance(f_or_data, str): f_or_data = f_or_data.encode() if isinstance(f_or_data, bytes): f_or_data = BytesIO(f_or_data) #TODO: the following line is the only one that needs readahead. #peek returns a arbitrary amount of bytes, so we have to slice. if f_or_data.seekable(): first_byte = f_or_data.read(1) f_or_data.seek(-1, SEEK_CUR) else: first_byte = f_or_data.peek(1)[:1] btype = TYPES.get(first_byte) if btype is not None: return btype(f_or_data) else: #Used in dicts and lists to designate an end assert_btype(f_or_data.read(1), _TYPE_END) return None
def from_bytes(cls, bytestr): # TODO: generify bio = BytesIO(bytestr) reader = ResponseReader() state = reader.state while True: if state is M.Complete: break elif state.type == M.NeedLine.type: line = bio.readline() # TODO: limit? next_state = M.HaveLine(value=line) elif state.type == M.NeedData.type: data = bio.read(state.amount) # TODO: can this block or return None if empty etc? next_state = M.HaveData(value=data) elif state.type == M.NeedPeek.type: peeked = bio.peek(state.amount) if not peeked: pass # TODO: again, what happens on end of stream next_state = M.HavePeek(amount=peeked) else: raise RuntimeError('Unknown state %r' % (state,)) state = reader.send(next_state) return reader.raw_response
def bdecode(f_or_data): """ bdecodes data by looking up the type byte, and using it to look up the respective decoding function, which in turn is used to return the decoded object The parameter can be a file opened in bytes mode, bytes or a string (the last of which will be decoded) """ if isinstance(f_or_data, str): f_or_data = f_or_data.encode() if isinstance(f_or_data, bytes): f_or_data = BytesIO(f_or_data) #TODO: the following line is the only one that needs readahead. #peek returns a arbitrary amount of bytes, so we have to slice. if f_or_data.seekable(): first_byte = f_or_data.read(1) f_or_data.seek(-1, SEEK_CUR) else: #FIXME: muted bug! first_byte = f_or_data.peek(1)[:1] # pylint: disable=no-member btype = TYPES.get(first_byte) if btype is not None: return btype(f_or_data) else: #Used in dicts and lists to designate an end assert_btype(f_or_data.read(1), _TYPE_END) return None
def deserialize(cls, list_: BytesIO, type_) -> typing.List: # We need to know the type of the objects contained in the list, # otherwise we'd have no idea how to deserialize it. # We wrap the BytesIO in a BufferedReader to get the .peek() method. list_ = BufferedReader(list_) length = int.from_bytes(list_.read(cls.size_length), "big") items = [] while list_.peek(1) != b"": i = type_.deserialize(list_) if i == b"": break items.append(i) return items
def parse(cls, string, ctx=None): if not isinstance(string, io.BufferedReader): if not isinstance(string, BytesIO): if isinstance(string, str): string = bytes(string, encoding="utf-8") string = BytesIO(string) string = io.BufferedReader(string) d = string.peek() big_enough = len(d) > 1 if big_enough: _c = cacher.retrieve(cls, d) if _c is not None: return _c if ctx is None: ctx = {} res = cls.parse_stream(string, ctx) if big_enough: cacher.set(cls, res, d) return res
def parse(cls, string, ctx=None): if not isinstance(string, io.BufferedReader): if not isinstance(string, BytesIO): if isinstance(string, str): string = bytes(string, encoding="utf-8") string = BytesIO(string) string = io.BufferedReader(string) d = string.peek() big_enough = len(d) > 1 if big_enough: k = cacher.get_key(d) _c = cacher.retrieve(cls, d) if _c is not None: #print("Returning cached item") return _c if ctx is None: ctx = OrderedDotDict() res = cls.parse_stream(string, ctx) if big_enough: cacher.set(cls, res, d) return res