У меня есть скрипт Python, который взаимодействует с консольным приложением (проприетарным для моей компании). Команде в этой консоли я должен открыть ее через Popen, а затем передать ей команду через stdin.write. Я реализовал не блокирующий потоковый ридер для потоковой передачи стандартного вывода консоли на консоль python в режиме реального времени. Это все отлично работает.
Проблема заключается в том, что консоль никогда не завершается и не отправляет код возврата (т.е. poll () всегда возвращает None, wait () зависает навсегда).
Поэтому я должен обработать вывод, чтобы определить, когда он закончил свою команду. Другое предостережение заключается в том, что команда, которую я посылаю через stdin.write (), на самом деле выполняет скрипт, который, в свою очередь, выполняет серию команд в консоли. Консоль остановится во время выполнения команд, и stdout.readline () на мгновение вернет ''. Так что я не могу использовать это, чтобы разорвать петлю. Код, который у меня работает, работает на 99%, но я не могу найти способ элегантного выхода, завершения подпроцесса и завершения потока в NonBlockingStreamReader. Итак, вот код:
# nbstreamready.py - source: http://eyalarubas.com/python-subproc-nonblock.html
from threading import Thread
from queue import Queue, Empty
class NonBlockingStreamReader:
def __init__(self, stream):
'''
stream: the stream to read from.
Usually a process' stdout or stderr.
'''
self._s = stream
self._q = Queue()
def _populateQueue(s, q):
'''
Collect lines from 'stream' and put them in 'quque'.
'''
while True:
line = s.readline()
if line:
q.put(line)
else:
raise UnexpectedEndOfStream
self._t = Thread(target=_populateQueue, args=(self._s, self._q), name='Thread-nsbr')
self._t.daemon = True
self._t.start() # start collecting lines from the stream
def readline(self, timeout=None):
try:
return self._q.get(block=timeout is not None, timeout=timeout)
except Empty:
return None
class UnexpectedEndOfStream(Exception):
pass
основной класс:
# etconsole.py
from subprocess import Popen, PIPE, STDOUT
from time import sleep
from nbstreamreader import NonBlockingStreamReader
from paths import scriptFolder
class ETConsole:
def __init__(self, scriptFileName, etInstallFolder='C:\\Program Files (x86)\\Engineering Tool 2010\\'):
self.exeFilePath = etInstallFolder + 'ConsoleEngineeringTool.exe'
self.scriptFile = scriptFolder + scriptFileName
self.process = Popen(self.exeFilePath, stdin=PIPE, stdout=PIPE, stderr=STDOUT)
self.nbsr = NonBlockingStreamReader(self.process.stdout)
self.nbsr.start()
def getoutput(self):
while True:
if self.process.returncode is not None:
print(f'ET Returncode: {self.process.returncode}')
if self.process.poll() is not None:
print(f'Poll: {self.process.returncode}')
output = self.nbsr.readline()
if output is not None:
outchar = output.decode().rstrip()
if 'Result executescript' in outchar:
print(outchar)
break
print(outchar)
def runscript(self):
sleep(.5)
cmd = 'executescript /import ' + self.scriptFile + '\n'
cmdbytes = bytes(cmd, 'utf-8')
# print("Command = " + cmd)
self.process.stdin.write(cmdbytes)
self.process.stdin.flush()
self.getoutput()
def terminate(self):
self.process.terminate()
поэтому, когда мы выполняем в консоли, метод getoutput () правильно прерывает стандартный цикл чтения цикла. Я процитировал и выделил потоковый стандартный вывод из моей консоли exe просто для ясности.
>>>from etconsole import ETConsole
et = ETConsole('RIGM-100012_testscript.txt')
et.runscript()
"ConsoleET>
### 2018-11-07 12:52:55 Command executescript
### 2018-11-07 12:52:55 Command setarchitecture
### 2018-11-07 12:52:55 Progress setarchitecture "" 0...10...20...30...40...50...60...70...80...90...100
### 2018-11-07 12:52:55 OCULAR-START setarchitecture
ApplicationMode = Tea2
### 2018-11-07 12:52:55 OCULAR-END setarchitecture
### 2018-11-07 12:52:55 Result setarchitecture 0
### 2018-11-07 12:52:55 Command login
### 2018-11-07 12:52:55 Progress login "" 0...10...20...30...40...50...60...70...80...90...100
### 2018-11-07 12:53:01 OCULAR-START login
LoginToSews = 0
### 2018-11-07 12:53:01 OCULAR-END login
### 2018-11-07 12:53:01 Result login 0
### 2018-11-07 12:53:02 Command connect
### 2018-11-07 12:53:02 Progress connect "" 0...10...20...30...40...50...60...70...80...90...100
### 2018-11-07 12:53:02 OCULAR-START connect
Connection-Status = 0
### 2018-11-07 12:53:02 OCULAR-END connect
### 2018-11-07 12:53:02 Result connect 0
### 2018-11-07 12:53:02 Command disconnect
### 2018-11-07 12:53:02 Progress disconnect "" 0...10...20...30...40...50...60...70...80...90...100
### 2018-11-07 12:53:02 OCULAR-START disconnect
Disconnect-Status = 0
### 2018-11-07 12:53:02 OCULAR-END disconnect
### 2018-11-07 12:53:02 Result disconnect 0
### 2018-11-07 12:53:02 Result executescript 0"
>>>
Это именно то, что я хочу, но это оставляет подпроцесс (в данном случае ConsoleEngineeringTool) запущенным (потому что я использовал stdin.write вместо коммуникации () и т. Д.) Так что давайте прервем процесс вручную:
>>>et.terminate()
Exception in thread Thread-nsbr:
Traceback (most recent call last):
File "C:\Program Files\Python\Python36\lib\threading.py", line 916, in _bootstrap_inner
self.run()
File "C:\Program Files\Python\Python36\lib\threading.py", line 864, in run
self._target(*self._args, **self._kwargs)
File "C:\Users\ru3944v\PycharmProjects\DP3_TestHelper\nbstreamreader_original.py", line 25, in _populateQueue
raise UnexpectedEndOfStream
nbstreamreader_original.UnexpectedEndOfStream
Я пытался использовать asyncio для создания потокового считывателя ссылка , но тоже не работает, он также ожидает в poll () что-то для возврата. Я также попытался сделать поток в nbstreamreader остановимым потоком и вызвать его событие остановки в главном классе, тоже не работает.
Так что мне нужна помощь с элегантным / безопасным способом: 1. закрыть подпроцесс exe и 2. закрыть поток в nonblockingstreamreader.
Большое спасибо.
P.S. Я работаю с Windows 7, Python 3.6. И да, я много искал ответы и пробовал много разных методов.