I am trying to create a subprocess of node.js in python to execute javascript code and read the output.
This code works in Windows 10 but on Ubuntu Linux it gives an error. When node starts it gives a prompt of >
on stdout and this code attempts to read that prompt to verify that node has started and that the pipe is readable but it doesn't work correctly on Linux.
import os
import subprocess
import time
import re
import json
import logging
logger = logging.getLogger(__name__)
if "nt" == os.name:
import msvcrt
from ctypes import windll, byref, wintypes, GetLastError, WinError
from ctypes.wintypes import HANDLE, DWORD, BOOL, LPDWORD
PIPE_NOWAIT = wintypes.DWORD(0x00000001)
ERROR_NO_DATA = 232
else:
import fcntl
class NodeEngine:
def __init__(self):
self.stdout_fd = None
self.p_stdout_fd = None
self.stdout = None
self.proc = None
self.__init_pipes()
def __init_pipes(self):
self.stdout_fd, self.p_stdout_fd = os.pipe()
self.pipe_no_wait(self.stdout_fd)
self.stdout = os.fdopen(self.p_stdout_fd,"w")
def pipe_no_wait(self, pipefd):
if "nt" == os.name:
SetNamedPipeHandleState = windll.kernel32.SetNamedPipeHandleState
SetNamedPipeHandleState.argtypes = [HANDLE, LPDWORD, LPDWORD, LPDWORD]
SetNamedPipeHandleState.restype = BOOL
h = msvcrt.get_osfhandle(pipefd)
res = windll.kernel32.SetNamedPipeHandleState(h, byref(PIPE_NOWAIT), None, None)
if res == 0:
print(WinError())
return False
return True
else:
fl = fcntl.fcntl(pipefd, fcntl.F_GETFL)
fcntl.fcntl(pipefd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
def start(self):
self.proc = subprocess.Popen(
["node","-i"],
stdin=subprocess.PIPE,
stdout=self.stdout,
# stderr=subprocess.PIPE,
shell=True
)
bytes_read = 0
ret = ""
timeout = time.time() + 10
while 0 == bytes_read or 1024 == bytes_read:
try:
data = os.read(self.stdout_fd,1024)
ret = ret + data.decode("utf-8")
bytes_read = len(data)
except Exception as e:
if time.time() >= timeout:
raise e
self.stdout.flush()
bytes_read = 0
def close(self):
try:
self.proc.stdin.close()
except:
pass
self.proc.terminate()
self.proc.wait(timeout=0.2)
if __name__ == '__main__':
engine = NodeEngine()
engine.start()
It seems the flush command isn't working. Is there a way to get this to work properly on Linux and Windows?
I found the bug and it was very subtle. When using Popen with shell=True the first argument shouldn't be a list.
I changed
self.proc = subprocess.Popen(
["node","-i"],
stdin=subprocess.PIPE,
stdout=self.stdout,
# stderr=subprocess.PIPE,
shell=True
)
to
self.proc = subprocess.Popen(
"node -i",
stdin=subprocess.PIPE,
stdout=self.stdout,
# stderr=subprocess.PIPE,
shell=True
)
and now it works on both Linux and Windows.
Note: I'll leave this question open for better solutions
User contributions licensed under CC BY-SA 3.0