Skip to content

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

Open
@sterlind

Description

@sterlind

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'))

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions