npmをインストールしている時のコンソールの出力を全て残そうと思ったけど出来なかった。
理由は、isTTY
で出し分けをしており、pythonのttyモジュールはwindowsでは使えないから。
やりたかった事
↓コマンド実行中に表示されるプログレスバー。コマンド完了時には消えてる。
これを全てテキストファイルに保存したかった。
した事
以下のコードでnpm install typescript
コマンドを実行した。
commands=["npm","install","typescript"]
def gl():
cp = subprocess.Popen(commands,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
with open("./stdout.txt","bw") as fh1,open("./stderr.txt","bw") as fh2:
while True:
if cp.stdout is None:
print("stdout is none")
break
if cp.stderr is None:
print("stderr is none")
break
line = cp.stdout.readline()
if line:
fh1.write(line)
fh1.flush()
yield line
if not line and cp.poll() is not None:
break
line = cp.stderr.readline()
if line:
fh2.write(line)
fh2.flush()
yield line
if not line and cp.poll() is not None:
break
for line in gt():
sys.stdout.write(line.decode("utf-8").strip()+"\n")
実行結果のファイルは以下の通り
+ typescript@3.9.7
updated 1 package and audited 1 package in 7.016s
found 0 vulnerabilities
npm WARN saveError ENOENT: no such file or directory, open 'D:\tmp_20200809Sun004215 \package.json'
npm WARN enoent ENOENT: no such file or directory, open 'D:\tmp_20200809Sun004215 \package.json'
npm WARN tmp_20200809Sun004215 No description
npm WARN tmp_20200809Sun004215 No repository field.
npm WARN tmp_20200809Sun004215 No README data
npm WARN tmp_20200809Sun004215 No license field.
原因
プログレスバーが表示されない
コンソールで出すには標準出力か、標準エラーのどちらか経由しか無いはず。という事は、npm側で出し分けをしている?
と思ってコードを追いかけてみたら、以下の箇所でした。
https://github.com/npm/cli/blob/ad8fe71a4f8c6d717375a6c699aaa00f11521adf/lib/npm.js#L349
if (config.get('progress') && process.stderr.isTTY && process.env['TERM'] !== 'dumb') {
log.enableProgress()
} else {
log.disableProgress()
}
python経由で実行した時はprocess.stderr.isTTY
の値がundefined
になっていた。cmd.exe経由ではtrue
。
ちなみに、無理やりprocess.stderr.isTTY=true
としたり、ifの判定を飛ばして実行してみたらターミナルが無反応になったので注意。ctrl+c すら吸い込まれた。おそらく、どこかの処理で別の事をしているのだろう。
isTTY をtrueにするには
pythonならやり方あるだろう。と思って調べたら以下の記事を発見
Filter out command that needs a terminal in Python subprocess module - Stack Overflow
回答の中に、疑似tty? を作る方法が書かれていた
https://stackoverflow.com/a/43012216/4314961 より転載
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os
import sys
import select
import termios
import tty
import pty
from subprocess import Popen
command = 'vim'
# save original tty setting then set it to raw mode
old_tty = termios.tcgetattr(sys.stdin)
tty.setraw(sys.stdin.fileno())
# open pseudo-terminal to interact with subprocess
master_fd, slave_fd = pty.openpty()
# use os.setsid() process the leader of a new session, or bash job control will not be enabled
p = Popen(command,
preexec_fn=os.setsid,
stdin=slave_fd,
stdout=slave_fd,
stderr=slave_fd,
universal_newlines=True)
while p.poll() is None:
r, w, e = select.select([sys.stdin, master_fd], [], [])
if sys.stdin in r:
d = os.read(sys.stdin.fileno(), 10240)
os.write(master_fd, d)
elif master_fd in r:
o = os.read(master_fd, 10240)
if o:
os.write(sys.stdout.fileno(), o)
# restore tty settings back
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_tty)
tty.setraw
とか、いかにもこれだよこれ。と思って実行してみたら、ダメ。
調べると、依存しているttyモジュールがunix限定。
Because it requires the termios module, it will work only on Unix.
https://docs.python.org/3/library/tty.html
まさかここでwindowsの2級市民っぷりを再認識する羽目になるとは。
どうすれば?
npmにプルリク出して、isTTYをオーバーライドする設定を追加してもらうとか。
pythonではどうしようもない感じなので、nodejsを使う。pty.js というライブラリがあるが
chjj/pty.js: Bindings to forkpty(3) for node.js.
最終更新が2016年の上、win10で動かないというissueも上がっていて微妙。
`npm install pty.js` failed on Windows 10 · Issue #151 · chjj/pty.js