pyomo icon indicating copy to clipboard operation
pyomo copied to clipboard

tee=True doesn't work with solvers that buffer stdout

Open sterlind opened this issue 2 years ago • 1 comments

Summary

Invoking CBC (at least version 2.10.3) through Pyomo with tee=True doesn't read cbc's command-line output as it's written - instead, nothing shows up until cbc finishes. Weirdly, this seems specific to CBC! SCIP actually works as you'd expect - you can tee its output and watch it come in.

This is caused (I think) by CBC buffering output on its side, because it detects that stdout is a pipe rather than a tty. Python shows this behavior, for example.

Steps to reproduce the issue

To reproduce with CBC, run:

sf = pe.SolverFactory("cbc")
sf.solve(model, tee=True)

For a minimal repro only on TeeStream, you can use this:

from pyomo.common.tee import TeeStream
import subprocess
import sys

cmd = "python -c \"import time; [(print(i+1), time.sleep(1)) for i in range(5)]\""
ostreams = [sys.stdout]
with TeeStream(*ostreams) as t:
    subprocess.run(
        cmd,
        shell=True,
        stdout=t.STDOUT,
        stderr=t.STDERR,
        universal_newlines=True,
        bufsize=0
    )
    
    t.STDOUT.flush()
    t.STDERR.flush()

Observe (minimal repro on TeeStream)

Output

Expected Output

(venv) sterlind@[redacted]:~/projects/[redacted]$ python notebooks/scratch.py
1
<1 sec delay>
2
<1 sec delay>
3
<...>
4
<...>
5

Actual Output

(venv) sterlind@[redacted]:~/projects/[redacted]$ python notebooks/scratch.py
<5 second delay>
1
2
3
4
5

Information on your system

Pyomo version: 6.5.0 Python version: 3.8.10 Operating system: Ubuntu 20.02 on Windows 11/WSL2 How Pyomo was installed (PyPI, conda, source): PyPI Solver (if applicable): CBC (possibly others, anything using isatty() or similar magic to set buffering)

Additional information

As a workaround, I use a cbc wrapper:

#!/bin/bash

# Use stdbuf -o0 to execute cbc with unbuffered output
exec stdbuf -o0 /usr/bin/cbc "$@"
from pyomo.common import Executable

# Set cbc path to cbc_wrapper.sh (in the same directory as this script.)
Executable('cbc').set_path(os.path.join(os.path.dirname(__file__), 'cbc_wrapper.sh'))

sterlind avatar Mar 22 '23 19:03 sterlind

@sterlind - We have been doing a lot of work on the tee system in the last few months. We are planning on cutting a release sometime this week (6.9.3). Would you be able to try this again next week with the newest version and let us know if the issue is still there?

mrmundt avatar Aug 04 '25 19:08 mrmundt