POPEN-коммуникация не работает

У меня есть скрипт, который исправно работает последние 3 месяца. Сервер отключился в прошлый понедельник, и с тех пор мой скрипт перестал работать. Скрипт зависает на coords = p.communicate()[0].split().

Вот часть скрипта:

class SelectByLatLon(GridSelector):
def __init__(self, from_lat, to_lat, from_lon, to_lon):
self.from_lat = from_lat
self.to_lat = to_lat
self.from_lon = from_lon
self.to_lon = to_lon

def get_selection(self, file):
p = subprocess.Popen(
        [
    os.path.join(module_root, 'bin/points_from_latlon.tcl'), 
    file, 
    str(self.from_lat), str(self.to_lat), str(self.from_lon), str(self.to_lon)
    ],
        stdout = subprocess.PIPE
    )
    coords = p.communicate()[0].split()
    return ZGridSelection(int(coords[0]), int(coords[1]), int(coords[2]), int(coords[3]))   

Когда я запускаю скрипт на другом сервере, все работает нормально. Могу ли я использовать что-то другое вместо p.communicate()[0].split() ?


person MrGRafael    schedule 15.07.2013    source источник
comment
Похоже, ваш tcl-скрипт висит. Исправьте это.   -  person martineau    schedule 15.07.2013
comment
Он «висит» бесконечно долго на communicate(), т.е. подпроцесс просто не завершается (вы должны следить за этим)? Различные серверы обычно подразумевают, что многие части среды, в которой работает программа, различны. Возможно, программа (подпроцесс) зависает, потому что ожидает ввода со стандартного ввода. Попробуйте открыть канал для стандартного ввода через stdin=subprocess.PIPE и предоставить некоторый ввод в подпроцесс (например, новую строку) через p.communicate("\n"). Если это поможет, позже мы сможем выяснить, что именно вызвало эту разницу.   -  person Dr. Jan-Philip Gehrcke    schedule 15.07.2013
comment
Мартино, ты прав. сценарий TCL вызывает проблему. Понятия не имею почему. тот же скрипт работал исправно последние 3 месяца. плохо попробуй разобраться.   -  person MrGRafael    schedule 15.07.2013


Ответы (1)


Возможно, вы ранее запускали свой сервер без демонизации, то есть у вас были функциональные потоки stdin, stdout, stderr. Чтобы исправить это, вы можете перенаправить потоки в DEVNULL для подпроцесса:

import os
from subprocess import Popen, PIPE

DEVNULL = os.open(os.devnull, os.O_RDWR)
p = Popen(tcl_cmd, stdin=DEVNULL, stdout=PIPE, stderr=DEVNULL, close_fds=True)
os.close(DEVNULL)

.communicate() может ожидать EOF на stdout, даже если tcl_cmd уже завершил работу: сценарий tcl мог породить дочерний процесс, который унаследовал стандартные потоки и пережил своего родителя.

Если вы знаете, что вам не нужен стандартный вывод после выхода из tcl_cmd, вы можете уничтожить все дерево процессов, когда обнаружите, что tcl_cmd выполнено.

Вам может понадобиться аналог start_new_session=True, чтобы убить все дерево процессов:

import os
import signal
from threading import Timer

def kill_tree_on_exit(p):
    p.wait() # wait for tcl_cmd to exit
    os.killpg(p.pid, signal.SIGTERM)

t = Timer(0, kill_tree_on_exit, [p])
t.start()
coords = p.communicate()[0].split()
t.cancel()

См. Как завершить подпроцесс python, запущенный с помощью shell=True.

person jfs    schedule 08.03.2014