I'm looking for a pythonic means of both reading and writing to a stream (in the IOBase hierarchy) without having to manage the stream position via seek() and tell(). This would be similar to how a socket stream would be expected to work where the receiving end would be able to continuously read while bytes where being received.
I've tried using a BufferedReader and BufferedWriter both attached to the same raw stream, but operations on one affect the stream position on the other. seek()/tell() appear to pass directly through to the underlying raw stream. Are there other IOBase types that can be used to act effectively as an IO stream where concurrent input and output are supported without the need to manage the stream position (similar to C++ stringstream >> and << ).
Thanks!
>>> import io
>>> buf = io.BytesIO()
>>> r = io.BufferedReader(buf)
>>> w = io.BufferedWriter(buf)
>>> w.write('foo bar')
7L
>>> r.read(1)
''
>>> r.tell()
0L
>>> w.flush()
>>> r.tell()
7L
>>> r.flush()
>>> r.tell()
7L
>>> w.tell()
7L
>>> buf.tell()
7L
You cannot directly.
A socket is anologous to a pair of file descriptors, one used for reading and one for writing. That's the reason why you are allowed to read and write on a a socket without using a seek between different operations.
If you really want to simulate a socket with StringIO
or BytesIO
, just build a custom class containing a pair or them.
It could be something like:
class BytesSocket(io.IOBase):
def __init__(self, inputText):
self.input = io.BytesIO(inputText)
self.output = io.BytesIO()
def read(self, n=-1):
return self.input.read(n)
def readinto(self, b):
return self.input.readinto(b)
def write(self, b):
return self.output.write(b)
def getoutvalue(self):
return self.output.getvalue()
If you need a loopback pseudo socket (read what has been previously written), you could use:
class BytesLoop(io.IOBase):
def __init__(self, inputText=''):
self.buf = inputText
def read(self, n=-1):
inp = io.BytesIO(self.buf)
b = inp.read(n)
self.buf = self.buf[len(b):]
return b
def readinto(self, b):
inp = io.BytesIO(buf)
l = inp.readinto(b)
self.buf = self.buf[l:]
return l
def write(self, b):
outp = io.BytesIO()
l = outp.write(b)
self.buf += outp.getvalue()
return l
def getvalue(self):
return self.buf
As a str
(or a unicode
) is a non mutable sequence, it needs to te re-written for each io operation. You could easily use instead a list of characters which is mutable. You can convert a string to a list with l = [c for c in s ]
and the opposite can be done with s = ''.join(l)
.
The following straightforward string example runs a fair amount faster than Serge's second example above (at least if the data held in the stream is relatively small) and works for simple read and writes to the same stream:
class BytesLoop:
def __init__(self, s=b''):
self.buffer = s
def read(self, n=-1):
chunk = self.buffer[:n]
self.buffer = self.buffer[n:]
return chunk
def write(self, s):
self.buffer += s
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With