That's a really good point. But (I reasoned, originally) the `read_*` functions have to do the `with` themselves, because their caller may return before the contents are consumed (and there's still the possibility of failing to consume all the tuples). So there are three possibilities here:

- What I have now: for both `read_*` and `write_*` functions, callers open files and pass them to the functions, but then don't need to close the files—but they do need to consume or close the generator returned by `read_*` functions.
- Let the `write_*` functions take file objects, but not close them—leave that to the callers—while the `read_*` functions do close the files.
- Make neither the `read_*` functions nor the `write_*` functions close the files they need, leaving that up to some caller far enough up the chain to know when the files can be safely closed.

When I wrote the code, I thought it was obvious that the first of these three was the right answer, but I'm not longer sure! You could be right that the second or third of these is actually a less error-prone interface, but I'm not entirely sure. None of them provides the strong guarantees on exception-safe file closing (in garbage-collected Pythons like PyPy) that I would like.

Although I hadn't realized this before, I think this is worth a paragraph or two in the book.