pytest: ValueError: I/O operation on closed file

Originally reported by: Alfredo Deza (BitBucket: alfredodeza, GitHub: alfredodeza)


There is a problem with the logging module and py.test

Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/logging/__init__.py", line 1472, in shutdown
Error in atexit._run_exitfuncs:
Traceback (most recent call last):
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/atexit.py", line 24, in _run_exitfuncs
    func(*targs, **kargs)
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/logging/__init__.py", line 1472, in shutdown
    h.flush()
  File "/System/Library/Frameworks/Python.framework/Versions/2.6/lib/python2.6/logging/__init__.py", line 740, in flush
    self.stream.flush()
    h.flush()
ValueError: I/O operation on closed file

It seems that if it runs with the ‘-s’ flag it doesn’t do this anymore.


About this issue

  • Original URL
  • State: closed
  • Created 14 years ago
  • Comments: 30 (24 by maintainers)

Commits related to this issue

Most upvoted comments

@nicoddemus @asottile

This is still an issue with the latest pytest across tests (e.g. when one tests change the logging config and add a stream handler pointing to stdout, a subsequent test that doesn’t setup logging and tries to write to logging will fail).

I think the issue boils down to the fact that if you set up a Stream Handler and you enable capsys the actual output stream will be a pytest stream that will be closed and thrown away at the next test in the test suite. So when a subsequent test tries to write to it you get this error. Adding the following fixture fixed this:

LOGGER = logging.getLogger()

@pytest.fixture(autouse=True)
def ensure_logging_framework_not_altered():
    before_handlers = list(LOGGER.handlers)
    yield
    LOGGER.handlers = before_handlers

Should this be automatically done always by pytest? Granted for this to work ideally you would need to do it for all loggers, not just the root one. In this case, for me, this was enough as I was only set up a custom stream logger onto the root logger.

In an ideal world, I would expect though pytest to cleanup loggig configurations at the end tests that requested the caplog fixture.

By the way, thank you for a great tool, all you pytest devs! Now I’ll shut up and stop spamming. ^.^

Thank you for your pointers! I have found if I leave stream=sys.stdout out of the call in the example, it works correctly: logging.basicConfig(level=logging.DEBUG). Interestingly, in my slightly more complex usecase it still didn’t work correctly despite doing that.

In the end, I found that removing the stream handler manually afterwards was enough to make the tests run as expected:

root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
hdlr = logging.StreamHandler()
root_logger.addHandler(hdlr)
# run stuff here...
root_logger.removeHandler(hdlr)

What is the correct usage pattern to do this with pytest?

Short answer (IMO) is to use pytest-catchlog which provides great integration with the logging package, including a caplog fixture to capture logging output.

Long answer:

The problem is (please correct me @RonnyPfannschmidt):

logging.basicConfig(level=logging.DEBUG, stream=sys.stdout)

sys.stdout at this point is a StringIO object installed by the capsys fixture.

You should “undo” what logging.basicConfig does when the test finishes, possibly in a fixture or a try/finally block.