問題

AtCoderのサンプルテスト用のCLIを作成している。

一部抜粋したコードが以下。

import subprocess

(...中略...)

result = subprocess.run(
    ["python3", f"contests/{args.contest}/{args.problem}.py"],
    input=input,
    capture_output=True,
    text=True,
    timeout=3,
)

timeoutを指定することによって、AtCoderでいうところのTLE(Time Limit Exceeded)を実現している。

タイムアウトしても処理が打ち切られる寸前までの標準出力を使ってデバッグをしたいことがあったので、以下のようなコードを書いて標準出力の中身を取得していた。

しかしこのコードを実行すると標準出力の中身が空になることが何度かあった。

import subprocess

(...中略...)

try:
    result = subprocess.run(
        ["python3", f"contests/{args.contest}/{args.problem}.py"],
        input=input,
        capture_output=True,
        text=True,
        timeout=3,
    )
except subprocess.TimeoutExpired as error:
    print_yellow(f"TLE: Test {i}")
    print_yellow(error.stdout.decode()) # これがまれにNoneになる

解決策

Pythonの標準出力はコードが終了するかバッファがいっぱいになるまではバッファに溜め込まれてしまう。

つまり出力量が少なく、かつタイムアウトになるようなコードの出力は標準出力にフラッシュされることなく闇に葬られてしまう。

そこで以下のようにバッファに溜め込まないように-uオプションを付けることで解決。

result = subprocess.run(
    ["python3", "-u", f"contests/{args.contest}/{args.problem}.py"],
    input=input,
    capture_output=True,
    text=True,
    timeout=3,
)

参考