Korzystam z modułu podprocesu, aby uruchomić podproces i połączyć się z jego strumieniem wyjściowym (standardowym wyjściem). Chcę mieć możliwość wykonywania nieblokujących odczytów na swoim standardowym wyjściu. Czy istnieje sposób, aby blokować .readline lub sprawdzić, czy w strumieniu znajdują się dane przed wywołaniem .readline
? Chciałbym, żeby to było przenośne lub przynajmniej działało pod Windows i Linux.
oto jak teraz to robię (blokuje, .readline
jeśli żadne dane nie są dostępne):
p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
python
io
subprocess
nonblocking
Mathieu Pagé
źródło
źródło
To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Odpowiedzi:
fcntl
,select
,asyncproc
Nie pomoże w tej sprawie.Niezawodny sposób na odczytanie strumienia bez blokowania niezależnie od systemu operacyjnego to użycie
Queue.get_nowait()
:źródło
out.readline
zablokowania wątku i głównego wątku, i muszę czekać, aż readline powróci, zanim wszystko inne będzie kontynuowane. Jest jakiś prosty sposób na obejście tego? (Czytam wiele wierszy z mojego procesu, który jest również innym plikiem .py, który robi DB i inne rzeczy)shelljob
pypi.python.org/pypi/shelljobCzęsto miałem podobny problem; Programy w języku Python, które piszę często muszą mieć możliwość wykonywania podstawowych funkcji, jednocześnie akceptując dane wejściowe użytkownika z wiersza poleceń (standardowe wejście). Po prostu umieszczenie funkcji obsługi danych wejściowych przez użytkownika w innym wątku nie rozwiązuje problemu, ponieważ
readline()
blokuje i nie ma limitu czasu. Jeśli podstawowa funkcjonalność jest kompletna i nie trzeba już czekać na dalsze dane wejściowe użytkownika, zwykle chcę, aby mój program zakończył działanie, ale nie może, ponieważreadline()
nadal blokuje się w drugim wątku, czekając na linię. Rozwiązaniem, które znalazłem dla tego problemu, jest uczynienie stdin plikiem nieblokującym za pomocą modułu fcntl:Moim zdaniem jest to nieco czystsze niż użycie modułów select lub signal do rozwiązania tego problemu, ale znowu działa tylko w systemie UNIX ...
źródło
buffer_size
zdefiniowane?Python 3.4 wprowadza nowe tymczasowe API dla asynchronicznego modułu IO
asyncio
.Podejście jest podobne do
twisted
opartej na odpowiedzi przez @Bryan Ward - zdefiniuj protokół, a jego metody będą wywoływane, gdy tylko dane będą gotowe:Zobacz „Podproces” w dokumentacji .
Istnieje interfejs wysokiego poziomu,
asyncio.create_subprocess_exec()
który zwracaProcess
obiekty które pozwalają na asynchroniczny odczyt linii za pomocąStreamReader.readline()
coroutine (ze składniąasync
/await
Python 3.5+ ):readline_and_kill()
wykonuje następujące zadania:W razie potrzeby każdy krok może być ograniczony limitem sekund.
źródło
print(text, flush=True)
aby wydrukowany tekst był natychmiast dostępny dla wywołującego obserwatorareadline
. Kiedy przetestowałem go z plikiem wykonywalnym opartym na Fortranie, tak naprawdę chcę go zawijać / oglądać, nie buforuje on swoich danych wyjściowych, więc zachowuje się zgodnie z oczekiwaniami.readline_and_kill
, w drugim skrypcie działa bardzo podobniesubprocess.comunicate
, ponieważ kończy proces po jednej operacji odczytu / zapisu. Widzę również, że używasz pojedynczego potokustdout
, który podproces obsługuje jako nieblokujący. Próbuję użyć obustdout
istderr
okazuje się, że blokuję .Wypróbuj moduł asyncproc . Na przykład:
Moduł zajmuje się wszystkimi wątkami, jak sugeruje S.Lott.
źródło
Możesz to zrobić naprawdę łatwo w Twisted . W zależności od istniejącej bazy kodu może nie być to łatwe w użyciu, ale jeśli budujesz pokręconą aplikację, rzeczy takie jak ta stają się prawie banalne. Tworzysz
ProcessProtocol
klasę i zastępujeszoutReceived()
metodę. Skręcona (w zależności od zastosowanego reaktora) jest zwykle tylko dużąselect()
pętlą z zainstalowanymi wywołaniami zwrotnymi do obsługi danych z różnych deskryptorów plików (często gniazd sieciowych). Tak więcoutReceived()
metodą jest po prostu instalacja wywołania zwrotnego do obsługi danych pochodzących zSTDOUT
. Prosty przykład demonstrujący to zachowanie jest następujący:Dokumentacja Twisted zawiera kilka dobrych informacji na ten temat.
Jeśli zbudujesz całą aplikację wokół Twisted, dzięki temu komunikacja asynchroniczna z innymi procesami, lokalnymi lub zdalnymi, będzie naprawdę elegancka. Z drugiej strony, jeśli twój program nie jest zbudowany na Twisted, to tak naprawdę nie będzie tak pomocne. Mamy nadzieję, że może to być pomocne dla innych czytelników, nawet jeśli nie dotyczy to konkretnej aplikacji.
źródło
select
nie powinien działać wselect()
on ma na myśli to samo, co ty. Zakładam, że toTwisted
działa, ponieważ działa wasyncio
stdlib .select()
jest najbardziej przenośny w systemach uniksowych i uniksowych, ale dla systemu Windows dostępne są również dwa reaktory: twistedmatrix.com/documents/current/core/howto/…Użyj wybierz i przeczytaj (1).
Dla readline () - jak:
źródło
select
nie powinien działać wproc.stdout.read()
bez względu na to, jak mały jest argument połączenie blokujące.OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
Jednym z rozwiązań jest wykonanie innego procesu w celu odczytania procesu lub wykonanie wątku procesu z przekroczeniem limitu czasu.
Oto wątkowa wersja funkcji limitu czasu:
http://code.activestate.com/recipes/473878/
Czy jednak musisz przeczytać standardowe wejście? Innym rozwiązaniem może być zrzucenie danych wyjściowych do pliku i oczekiwanie na zakończenie procesu za pomocą p.wait () .
źródło
Oświadczenie: działa tylko w przypadku tornada
Możesz to zrobić, ustawiając fd na nieblokujący, a następnie użyj ioloop, aby zarejestrować wywołania zwrotne. Zapakowałem to w jajko o nazwie tornado_subprocess i możesz je zainstalować za pomocą PyPI:
teraz możesz zrobić coś takiego:
możesz także użyć go z RequestHandler
źródło
threading.Thread
do tworzenia nowych nieblokujących procesów? Użyłem go won_message
instancji websocket Tornado i dobrze się spisało.select
przy deskryptorach plików tak nie jest )select
połączenia. Nie próbowałem tego w systemie Windows, ale prawdopodobnie miałbyś kłopoty, ponieważ lib używafcntl
modułu. Krótko mówiąc: nie, to prawdopodobnie nie będzie działać w systemie Windows.Istniejące rozwiązania nie działały dla mnie (szczegóły poniżej). W końcu udało się zaimplementować readline przy użyciu read (1) (na podstawie tej odpowiedzi ). Ten ostatni nie blokuje:
Dlaczego istniejące rozwiązania nie działały:
źródło
q.get_nowait()
z mojej odpowiedzi nie wolno nigdy blokować, to jest sens jej użycia. 2. Wątek, który wykonuje readline (enqueue_output()
funkcję ), wychodzi na EOF, np. Łącznie z przypadkiem, w którym proces produkcji wyjściowej został zabity. Jeśli uważasz, że tak nie jest; podaj kompletny minimalny przykład kodu, który pokazuje inaczej (być może jako nowe pytanie ).dcmpid = myprocess
.Oto mój kod, używany do przechwytywania każdego wyjścia z podprocesu jak najszybciej, w tym wierszy częściowych. Pompuje jednocześnie, stdout i stderr w prawie prawidłowej kolejności.
Testowane i poprawnie działające na systemie Linux i Windows Python 2.7.
źródło
Dodaję ten problem, aby przeczytać niektóre podprocesy. Otwórz stdout. Oto moje nieblokujące rozwiązanie odczytu:
źródło
msvcrt.kbhit()
zamiast tego użyjTa wersja nieblokującego odczytu nie wymaga specjalnych modułów i będzie działać od razu po instalacji w większości dystrybucji Linuksa.
źródło
Oto proste rozwiązanie oparte na wątkach, które:
select
).stdout
istderr
asynchronicznie.asyncio
(co może powodować konflikt z innymi bibliotekami).printer.py
reader.py
źródło
Dodając tę odpowiedź tutaj, ponieważ zapewnia ona możliwość ustawiania nieblokujących potoków w systemach Windows i Unix.
Wszystkie
ctypes
szczegóły są dzięki odpowiedzi @ techtonik .Istnieje nieco zmodyfikowana wersja do użytku zarówno w systemach Unix, jak i Windows.
W ten sposób możesz użyć tej samej funkcji i wyjątku dla kodu Unix i Windows.
Aby uniknąć odczytu niekompletnych danych, ostatecznie napisałem własny generator readline (który zwraca ciąg bajtów dla każdej linii).
Jest to generator, dzięki czemu możesz na przykład ...
źródło
readline()
nie działa z potokami nieblokującymi (takimi jak ustawianie za pomocąfcntl
) w Pythonie 2 - czy uważasz, że nie jest już poprawny? (moja odpowiedź zawiera link (fcntl
), który zawiera te same informacje, ale wydaje się teraz usunięty). (2) Zobacz, jakmultiprocessing.connection.Pipe
używaSetNamedPipeHandleState
Mam problem z pierwotnym pytającym, ale nie chciałem powoływać się na wątki. Zmieszałem rozwiązanie Jesse z bezpośrednim odczytem () z potoku i moim własnym programem obsługi buforów do odczytu linii (jednak mój podproces - ping - zawsze zapisywał pełne linie <rozmiar strony systemowej). Unikam zajętego oczekiwania, czytając tylko zegarek IO zarejestrowany przez gobject. Obecnie zwykle uruchamiam kod w gobject MainLoop, aby uniknąć wątków.
Obserwatorem jest
Główny program ustawia polecenie ping, a następnie wywołuje pętlę poczty gobject.
Wszelkie inne prace są dołączane do wywołań zwrotnych w gobject.
źródło
We współczesnym Pythonie jest znacznie lepiej.
Oto prosty program potomny „hello.py”:
I program do interakcji z nim:
To drukuje:
Zauważ, że faktyczny wzorzec, który jest również prawie wszystkimi poprzednimi odpowiedziami, zarówno tutaj, jak i w powiązanych pytaniach, polega na ustawieniu deskryptora standardowego pliku dziecka na nieblokujący, a następnie odpytaniu go w jakiejś pętli wyboru. Obecnie ta pętla jest zapewniona przez asyncio.
źródło
wybierz moduł pozwala określić, gdzie obok wejścia jest przydatna.
Jednak prawie zawsze jesteś szczęśliwszy dzięki oddzielnym wątkom. Jeden robi blokowanie, czytając standardowe wejście, inny robi to, gdzie nie ma potrzeby blokowania.
źródło
po co męczyć wątek i kolejkę? w przeciwieństwie do readline (), BufferedReader.read1 () nie blokuje czekania na \ r \ n, zwraca JAK NAJSZYBCIEJ, jeśli pojawi się jakikolwiek wynik.
źródło
read1
zablokuje się, jeśli pierwsze leżące poniżej bloki odczytu, co dzieje się, gdy potok jest nadal otwarty, ale dane wejściowe nie są dostępne.W moim przypadku potrzebowałem modułu rejestrującego, który przechwytuje dane wyjściowe z aplikacji w tle i rozszerza je (dodając znaczniki czasu, kolory itp.).
Skończyło się na wątku w tle, który wykonuje rzeczywiste operacje we / wy. Poniższy kod dotyczy tylko platform POSIX. Pozbyłem się nieistotnych części.
Jeśli ktoś zamierza używać tej bestii na dłuższą metę, rozważ zarządzanie otwartymi deskryptorami. W moim przypadku nie był to duży problem.
źródło
Mój problem jest nieco inny, ponieważ chciałem zebrać zarówno stdout, jak i stderr z uruchomionego procesu, ale ostatecznie taki sam, ponieważ chciałem renderować dane wyjściowe w widgecie po jego wygenerowaniu.
Nie chciałem uciekać się do wielu proponowanych obejść przy użyciu kolejek lub dodatkowych wątków, ponieważ nie powinny one być konieczne do wykonania tak typowego zadania, jak uruchomienie innego skryptu i zebranie jego danych wyjściowych.
Po przeczytaniu proponowanych rozwiązań i dokumentów w języku Python rozwiązałem problem z implementacją poniżej. Tak, działa tylko dla POSIX, ponieważ używam
select
wywołania funkcji.Zgadzam się, że dokumenty są mylące, a implementacja jest niezręczna w przypadku tak powszechnego zadania skryptowego. Uważam, że starsze wersje Pythona mają różne domyślne ustawienia
Popen
i różne wyjaśnienia, co spowodowało wiele zamieszania. Wydaje się, że działa to dobrze zarówno w Pythonie 2.7.12, jak i 3.5.2.Kluczem było ustawienie
bufsize=1
buforowania linii, a następnieuniversal_newlines=True
przetwarzanie jako plik tekstowy zamiast pliku binarnego, który wydaje się być domyślny podczas ustawianiabufsize=1
.ERROR, DEBUG i VERBOSE to po prostu makra, które wypisują dane wyjściowe na terminal.
To rozwiązanie jest skuteczne IMHO 99,99%, ponieważ nadal korzysta z
readline
funkcji blokowania , więc zakładamy, że podproces jest przyjemny i generuje pełne linie.Czekam na opinie w celu ulepszenia rozwiązania, ponieważ wciąż jestem nowy w Pythonie.
źródło
Stworzyłem bibliotekę opartą na rozwiązaniu JF Sebastiana . Możesz tego użyć.
https://github.com/cenkalti/what
źródło
Korzystając z odpowiedzi JF Sebastiana i kilku innych źródeł, stworzyłem prostego menedżera podprocesów. Zapewnia nieblokujący odczyt żądania, a także uruchamia kilka procesów równolegle. Nie używa żadnego połączenia specyficznego dla systemu operacyjnego (o czym wiem) i dlatego powinien działać wszędzie.
Jest dostępny w pypi, więc po prostu
pip install shelljob
. Przykłady i pełne dokumenty znajdują się na stronie projektu .źródło
EDYCJA: Ta implementacja wciąż blokuje. Zamiast tego użyj odpowiedzi JFSebastian .
Wypróbowałem najlepszą odpowiedź , ale dodatkowe ryzyko i utrzymanie kodu wątku były niepokojące.Przeglądając moduł io (i jest ograniczony do 2.6), znalazłem BufferedReader. To jest moje bezgwintowe, nieblokujące rozwiązanie.źródło
for line in iter(p.stdout.readline, ""): # do stuff with the line
? Jest bezgwintowy (pojedynczy wątek) i blokuje się, gdy kod jest blokowany.Niedawno natknąłem się na ten sam problem: muszę czytać jedną linię na raz ze strumienia (uruchamianie ogona w podprocesie) w trybie nieblokującym. Chciałem uniknąć kolejnych problemów: nie nagrywać procesora, nie czytać strumienia o jeden bajt ( tak jak readline) itp
Oto moja implementacja https://gist.github.com/grubberr/5501e1a9760c3eab5e0a nie obsługuje okien (ankiety), nie obsługuje EOF, ale działa dobrze dla mnie
źródło
timeout
jak w roztworze) i.readline()
czyta więcej niż jeden bajt na raz (bufsize=1
środki liniowych -buffered (dotyczy tylko pisemnie)). Jakie inne problemy znalazłeś? Odpowiedzi zawierające tylko łącze nie są zbyt przydatne.Jest to przykład uruchomienia komendy interaktywnej w podprocesie, a standardowe wyjście jest interaktywne przy użyciu pseudo terminala. Możesz odnieść się do: https://stackoverflow.com/a/43012138/3555925
źródło
To rozwiązanie wykorzystuje
select
moduł do „odczytu dowolnych dostępnych danych” ze strumienia IO. Ta funkcja początkowo blokuje się, dopóki dane nie będą dostępne, ale następnie odczytuje tylko te dane, które są dostępne, i nie blokuje dalej.Biorąc pod uwagę fakt, że używa on
select
modułu, działa to tylko na Uniksie.Kod jest w pełni zgodny z PEP8.
źródło
Napotkałem również problem opisany przez Jesse'a i rozwiązałem go, używając „select”, tak jak robili to Bradley , Andy i inni, ale w trybie blokowania, aby uniknąć zajętej pętli. Wykorzystuje atrapę Fajka jako fałszywego standardowego wejścia. Wybierz bloki i poczekaj, aż standardowe wejście lub rura będą gotowe. Po naciśnięciu klawisza stdin odblokowuje wybór, a wartość klucza można odczytać za pomocą read (1). Gdy do gwintu zostanie zapisany inny wątek, wówczas rura odblokowuje zaznaczenie i można to uznać za wskazówkę, że zapotrzebowanie na standardowe wyjście zostało zakończone. Oto kod referencyjny:
źródło
Wypróbuj wexpect , który jest alternatywą systemu pexpect dla systemu Windows .
źródło
W systemach uniksowych i Python 3.5+ istnieje
os.set_blocking
dokładnie to, co mówi.To daje:
Z
os.set_blocking
komentarzem to:źródło
Oto moduł obsługujący nieblokujące odczyty i zapisy w tle w pythonie:
https://pypi.python.org/pypi/python-nonblock
Zapewnia funkcję,
nonblock_read, który odczyta dane ze strumienia, jeśli jest dostępny, w przeciwnym razie zwróci pusty ciąg (lub Brak, jeśli strumień jest zamknięty po drugiej stronie i wszystkie możliwe dane zostały odczytane)
Możesz również rozważyć moduł python-subprocess2,
https://pypi.python.org/pypi/python-subprocess2
co dodaje moduł podprocesu. Tak więc do obiektu zwróconego z „subprocess.Popen” dodaje się dodatkową metodę runInBackground. To uruchamia wątek i zwraca obiekt, który zostanie automatycznie zapełniony, gdy rzeczy są zapisywane do stdout / stderr, bez blokowania głównego wątku.
Cieszyć się!
źródło