Существует решение с использованием оболочки (как описано в ссылке, предоставленной Vinay), которая запускается в новом окне консоли с помощью команды Windows start .
Код оболочки:
#wrapper.py
import subprocess, time, signal, sys, os
def signal_handler(signal, frame):
time.sleep(1)
print 'Ctrl+C received in wrapper.py'
signal.signal(signal.SIGINT, signal_handler)
print "wrapper.py started"
subprocess.Popen("python demo.py")
time.sleep(3) #Replace with your IPC code here, which waits on a fire CTRL-C request
os.kill(signal.CTRL_C_EVENT, 0)
Код программы, перехватывающей CTRL-C:
#demo.py
import signal, sys, time
def signal_handler(signal, frame):
print 'Ctrl+C received in demo.py'
time.sleep(1)
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
print 'demo.py started'
#signal.pause() # does not work under Windows
while(True):
time.sleep(1)
Запустите оболочку, например, например:
PythonPrompt> import subprocess
PythonPrompt> subprocess.Popen("start python wrapper.py", shell=True)
Вам необходимо добавить код IPCкоторый позволяет вам управлять оболочкой, используя команду os.kill (signal.CTRL_C_EVENT, 0).Я использовал для этой цели сокеты в своем приложении.
Объяснение:
Предварительная информация
send_signal(CTRL_C_EVENT)
не работает, поскольку CTRL_C_EVENT
только для os.kill
. [REF1] os.kill(CTRL_C_EVENT)
отправляет сигнал всем процессам, запущенным в текущем окне cmd [REF2] Popen(..., creationflags=CREATE_NEW_PROCESS_GROUP)
неработать, потому что CTRL_C_EVENT
игнорируется для групп процессов. [REF2] Это ошибка в документации по питону [REF3]
Реализованное решение
- Дайте вашей программе работатьв другом окне cmd с помощью команды оболочки Windows start .
- Добавьте оболочку запроса CTRL-C между вашим управляющим приложением и приложением, которое должно получить сигнал CTRL-C.Оболочка будет работать в том же окне cmd, что и приложение, которое должно получить сигнал CTRL-C.
- Оболочка сама отключится, и программа, которая должна получить сигнал CTRL-C, отправит все процессы в cmd.окно CTRL_C_EVENT.
- Управляющая программа должна иметь возможность запрашивать оболочку для отправки сигнала CTRL-C.Это может быть реализовано через IPC, например, сокеты.
Полезные сообщения были:
Мне пришлось удалить http перед ссылками, потому что я новыйПользователь и не имеет права публиковать более двух ссылок.
Обновление: оболочка CTRL-C на основе IPC
Здесь вы можете найти самописный модуль python, обеспечивающий упаковку CTRL-C, включая IPC на основе сокетов.Синтаксис очень похож на модуль подпроцесса.
Использование:
>>> import winctrlc
>>> p1 = winctrlc.Popen("python demo.py")
>>> p2 = winctrlc.Popen("python demo.py")
>>> p3 = winctrlc.Popen("python demo.py")
>>> p2.send_ctrl_c()
>>> p1.send_ctrl_c()
>>> p3.send_ctrl_c()
Код
import socket
import subprocess
import time
import random
import signal, os, sys
class Popen:
_port = random.randint(10000, 50000)
_connection = ''
def _start_ctrl_c_wrapper(self, cmd):
cmd_str = "start \"\" python winctrlc.py "+"\""+cmd+"\""+" "+str(self._port)
subprocess.Popen(cmd_str, shell=True)
def _create_connection(self):
self._connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._connection.connect(('localhost', self._port))
def send_ctrl_c(self):
self._connection.send(Wrapper.TERMINATION_REQ)
self._connection.close()
def __init__(self, cmd):
self._start_ctrl_c_wrapper(cmd)
self._create_connection()
class Wrapper:
TERMINATION_REQ = "Terminate with CTRL-C"
def _create_connection(self, port):
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('localhost', port))
s.listen(1)
conn, addr = s.accept()
return conn
def _wait_on_ctrl_c_request(self, conn):
while True:
data = conn.recv(1024)
if data == self.TERMINATION_REQ:
ctrl_c_received = True
break
else:
ctrl_c_received = False
return ctrl_c_received
def _cleanup_and_fire_ctrl_c(self, conn):
conn.close()
os.kill(signal.CTRL_C_EVENT, 0)
def _signal_handler(self, signal, frame):
time.sleep(1)
sys.exit(0)
def __init__(self, cmd, port):
signal.signal(signal.SIGINT, self._signal_handler)
subprocess.Popen(cmd)
conn = self._create_connection(port)
ctrl_c_req_received = self._wait_on_ctrl_c_request(conn)
if ctrl_c_req_received:
self._cleanup_and_fire_ctrl_c(conn)
else:
sys.exit(0)
if __name__ == "__main__":
command_string = sys.argv[1]
port_no = int(sys.argv[2])
Wrapper(command_string, port_no)