Комуникацията на 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. Опитайте да отворите канал към stdin чрез 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 скриптът може да е породил дъщерен процес, който е наследил стандартните потоци и е надживел своя родител.

Ако знаете, че не се нуждаете от stdout след излизане от 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