Sunday, January 31, 2010

Flush to the Rescue, None is not zero

Well, that was quick.  (See my previous blog post about socket.makefile() in Python 3.1.1). I was sent a message to try the flush() method on the file object after attempting to write to it.  Indeed that works for both the binary io.BufferedWriter and text based io.TextIOWraper.  We usually think about using a I/O flush() method to switch between writing and reading operations, but here it is needed just to force the message to be sent instead of being held up in the buffer.

So the only question remaining is on the default value for the buffering parameter to makefile().  The following results in a buffered objects, which is what one might expect:

fd = s.makefile('w') -> buffered io.TextIOWrapper
fd = s.makefile('wb') -> buffered io.BufferedWriter

However, the default value for buffering is None, so the above two statements are equivalent to the following two, which one might think would would return an unbuffered object rather than the buffered objects returned just as above.

fd = s.makefile('w', buffering = None) -> buffered io.TextIOWrapper
fd = s.makefile('wb', buffering = None) -> buffered io.BufferedWriter

Perhaps it would be more obvious to list the default value of buffering as True, with maybe another parameter for the default buffer size.  I won't hold my breath for that change.   I'd be happy to see the documentation updated to reflect that buffering must be set to zero (buffering = 0), to get an unbuffered file object from socket.makefile()

Tricky issues with socket.makefile() in Python 3

I have been experimenting with socket.makefile()from Python 3.1.1.
The makefile() method returns a file like object for the socket so that file operations (read, readlines, write, writelines, ...) can be used to send and receive data on the socket.

I have not had much difficulty reading from the returned file object, but I don't understand the behavior when trying to write (send on the socket).  I'm hoping that someone can explain how this is supposed to work.

I find that this works for an established connection on socket s:
fd = s.makefile('wb', buffering = 0)
fd.write("This is a test message\n".encode('ascii'))

A mode of 'rwb' also works.  The object fd is of type SocketIO.

fd = s.makefile('w', buffering = 0) -> ValueError exception
fd = s.makefile('w') -> io.BufferedWriter, which does not send data.
fd = s.makefile('wb') -> io.TextIOWrapper, which does not send data.

The default value of the "buffering" parameter is None, which from my testing has a different result than 0 (zero).

So, questions:
1) Why does buffering = None result in a buffered file object?
2) Are there bugs or incomplete work with socket.makefile(), io.BufferedWriter and io.TextIOWrapper in terms of why the latter two objects are returned, but fail to send data?

If I find out the answers to these questions, I'll post them.