web-dev-qa-db-ja.com

JupyterノートブックのPythonサブプロセスからのライブstdout出力

サブプロセスを使用して、Jupyterノートブックで実行しているPython(3.5.2)スクリプトからコマンドラインプログラムを実行しています。サブプロセスの実行には時間がかかるため、その標準出力をJupyterノートブックの画面にライブで印刷したいと考えています。

私はこれを通常のPythonスクリプトをターミナルから実行することで問題なく実行できます。

def run_command(cmd):
from subprocess import Popen, PIPE
import shlex

with Popen(shlex.split(cmd), stdout=PIPE, bufsize=1, universal_newlines=True) as p:
    for line in p.stdout:
        print(line, end='')
    exit_code = p.poll()
return exit_code

ただし、Jupyterノートブックでスクリプトを実行すると、標準出力がライブで画面に出力されません。代わりに、サブプロセスの実行が終了した後にすべてを出力します。

これを改善する方法について何かアイデアはありますか?

どうもありがとう、ジョニー

18
Johnny Hunter

Ipythonノートブックには独自の シェルコマンドの実行のサポート があります。サブプロセスのものでキャプチャする必要がない場合は、単に行うことができます

cmd = 'ls -l'
!{cmd}

!で実行されたコマンドからの出力自動的にノートブックにパイプされます。

15
cdleonard

stdout = Noneを設定すると(これがデフォルトなので、stdout引数を完全に省略できます)、プロセスはその出力をIPythonノートブックサーバーを実行しているターミナルに書き込む必要があります。

これは、デフォルトの動作ではサブプロセスが親ファイルハンドラーを継承するためです( docs を参照)。

コードは次のようになります。

from subprocess import Popen, PIPE
import shlex

def run_command(cmd):
    p = Popen(shlex.split(cmd), bufsize=1, universal_newlines=True)
    return p.poll()

これはブラウザーのノートブックに出力されませんが、少なくとも、他のコードが実行されている間、サブプロセスからの出力を非同期で見ることができます。

お役に立てれば。

3
FluxLemur

Stdoutとstderrを備えたJupyterのマック。これはあなたが望むものを取得し、コマンドが起動に失敗した場合により有用な例外を与えます。

import signal
import subprocess as sp


class VerboseCalledProcessError(sp.CalledProcessError):
    def __str__(self):
        if self.returncode and self.returncode < 0:
            try:
                msg = "Command '%s' died with %r." % (
                    self.cmd, signal.Signals(-self.returncode))
            except ValueError:
                msg = "Command '%s' died with unknown signal %d." % (
                    self.cmd, -self.returncode)
        else:
            msg = "Command '%s' returned non-zero exit status %d." % (
                self.cmd, self.returncode)

        return f'{msg}\n' \
               f'Stdout:\n' \
               f'{self.output}\n' \
               f'Stderr:\n' \
               f'{self.stderr}'


def bash(cmd, print_stdout=True, print_stderr=True):
    proc = sp.Popen(cmd, stderr=sp.PIPE, stdout=sp.PIPE, Shell=True, universal_newlines=True,
                    executable='/bin/bash')

    all_stdout = []
    all_stderr = []
    while proc.poll() is None:
        for stdout_line in proc.stdout:
            if stdout_line != '':
                if print_stdout:
                    print(stdout_line, end='')
                all_stdout.append(stdout_line)
        for stderr_line in proc.stderr:
            if stderr_line != '':
                if print_stderr:
                    print(stderr_line, end='', file=sys.stderr)
                all_stderr.append(stderr_line)

    stdout_text = ''.join(all_stdout)
    stderr_text = ''.join(all_stderr)
    if proc.wait() != 0:
        raise VerboseCalledProcessError(proc.returncode, cmd, stdout_text, stderr_text)
0
egafni