Oto kod Pythona, aby uruchomić dowolne polecenie zwracające jego stdout
dane lub zgłosić wyjątek od niezerowych kodów wyjścia:
proc = subprocess.Popen(
cmd,
stderr=subprocess.STDOUT, # Merge stdout and stderr
stdout=subprocess.PIPE,
shell=True)
communicate
służy do oczekiwania na zakończenie procesu:
stdoutdata, stderrdata = proc.communicate()
subprocess
Moduł nie obsługuje limit czasu - zdolność do zabijania procesu uruchomionego przez więcej niż x liczbę sekund - w związku z tym communicate
może mieć wiecznie uciekać.
Jaki jest najprostszy sposób na wdrożenie limitów czasu w programie Python przeznaczonym do uruchamiania w systemach Windows i Linux?
python
multithreading
timeout
subprocess
Sridhar Ratnakumar
źródło
źródło
Odpowiedzi:
W Python 3.3+:
output
jest ciągiem bajtów, który zawiera połączone stdout polecenia, dane stderr.check_output
podnosiCalledProcessError
niezerowy status wyjścia, jak określono w tekście pytania, w przeciwieństwie doproc.communicate()
metody.Usunąłem,
shell=True
ponieważ często jest używany niepotrzebnie. Zawsze możesz go dodać z powrotem, jeślicmd
rzeczywiście tego wymaga. Jeśli dodaszshell=True
tj. Jeśli proces potomny spawnuje swoich potomków;check_output()
może powrócić znacznie później niż wskazuje limit czasu, zobacz Błąd przekroczenia limitu czasu podprocesu .Funkcja limitu czasu jest dostępna w Pythonie 2.x poprzez
subprocess32
backport modułu podprocesora 3.2+.źródło
check_output()
jest preferowanym sposobem uzyskania danych wyjściowych (zwraca dane wyjściowe bezpośrednio, nie ignoruje błędów, jest dostępne od zawsze). 2-run()
jest bardziej elastyczny, alerun()
domyślnie ignoruje błąd i wymaga dodatkowych kroków, aby uzyskać wynik 3-check_output()
jest zaimplementowany pod względemrun()
i dlatego przyjmuje większość takich samych argumentów. 4-nit:capture_output
jest dostępny od 3.7, a nie 3.5Nie wiem dużo o szczegółach niskiego poziomu; ale biorąc pod uwagę, że w Pythonie 2.6 API oferuje możliwość czekania na wątki i kończenia procesów, co z uruchomieniem procesu w osobnym wątku?
Dane wyjściowe tego fragmentu w mojej maszynie to:
gdzie widać, że w pierwszym wykonaniu proces zakończył się poprawnie (kod powrotu 0), podczas gdy w drugim proces został zakończony (kod powrotu -15).
Nie testowałem w systemie Windows; ale oprócz aktualizacji przykładowego polecenia, myślę, że powinno działać, ponieważ nie znalazłem w dokumentacji niczego, co mówi, że Thread.join lub Process.terminate nie jest obsługiwane.
źródło
Odpowiedź jcollado można uprościć za pomocą klasy threading.Timer :
źródło
lambda
:t = Timer(timeout, proc.kill)
timer.isAlive()
wcześniejtimer.cancel()
oznacza, że zakończyło się normalnieJeśli korzystasz z Uniksa,
źródło
Oto rozwiązanie Alexa Martellego jako moduł z odpowiednim zabijaniem procesów. Inne podejścia nie działają, ponieważ nie używają proc.communicate (). Więc jeśli masz proces, który produkuje dużo danych wyjściowych, zapełni bufor wyjściowy, a następnie zablokuje, dopóki czegoś z niego nie przeczytasz.
źródło
ValueError: signal only works in main thread
Zmodyfikowałem odpowiedź sussudio . Teraz zwraca: (
returncode
,stdout
,stderr
,timeout
) -stdout
istderr
jest dekodowany do UTF-8 ciągźródło
zaskoczony, nikt nie wspomniał o użyciu
timeout
timeout 5 ping -c 3 somehost
To oczywiście nie zadziała w każdym przypadku użycia, ale jeśli masz do czynienia z prostym skryptem, trudno go pokonać.
Dostępny również jako gtimeout w Coreutils przez
homebrew
dla użytkowników komputerów Mac.źródło
proc = subprocess.Popen(['/usr/bin/timeout', str(timeout)] + cmd, ...)
. Czytimeout
w systemie Windows jest polecenie, jak pyta OP?timeout
jest teraz obsługiwany przezcall()
icommunicate()
w module podprocesu (od Python3.3):Spowoduje to wywołanie polecenia i zgłoszenie wyjątku
jeśli polecenie nie zakończy się po 20 sekundach.
Następnie możesz obsłużyć wyjątek, aby kontynuować kod, na przykład:
Mam nadzieję że to pomoże.
źródło
timeout
parametrze . Chociaż powtórzenie tego nie zaszkodzi.Inną opcją jest zapisanie do pliku tymczasowego, aby zapobiec blokowaniu standardowego wyjścia zamiast konieczności odpytywania za pomocą komunikacji (). To zadziałało dla mnie, gdy inne odpowiedzi nie; na przykład w systemie Windows.
źródło
Nie wiem, dlaczego to nie wymieniono, ale ponieważ Python 3.5, nowe
subprocess.run
uniwersalne polecenia (które ma zastąpićcheck_call
,check_output
...) i który matimeout
parametr, jak również.Zgłasza
subprocess.TimeoutExpired
wyjątek, gdy upłynął limit czasu.źródło
Oto moje rozwiązanie, korzystałem z wątku i zdarzenia:
W akcji:
źródło
Rozwiązaniem, którego używam, jest poprzedzenie polecenia powłoki limitem czasowym . Jeśli polecenie trwa zbyt długo, limit czasu go zatrzyma, a Popen będzie miał ustawiony kod powrotu ustawiony przez timelimit. Jeśli jest> 128, oznacza to, że limit czasu zabił proces.
Zobacz także podprocesor Pythona z limitem czasu i dużą wydajnością (> 64 KB)
źródło
timeout
- packages.ubuntu.com/search?ke words=timeout - ale żadne z nich nie działa w systemie Windows, prawda?Dodałem rozwiązanie z wątkami od
jcollado
do łatwego procesu modułu Pythona .Zainstalować:
Przykład:
źródło
jeśli używasz Pythona 2, spróbuj
źródło
Przygotowanie polecenia Linux
timeout
nie jest złym obejściem i działało dla mnie.źródło
Zaimplementowałem to, co mogłem zebrać z kilku z nich. Działa to w systemie Windows, a ponieważ jest to wiki społeczności, myślę, że udostępnię również mój kod:
Następnie z innej klasy lub pliku:
źródło
terminate()
znaki funkcyjne wątek jako zakończone, ale faktycznie nie zakończyć wątek! Mogę to sprawdzić w * nix, ale nie mam komputera z systemem Windows do przetestowania.Gdy zrozumiesz pełną maszynę do obsługi procesów w * unix, z łatwością znajdziesz prostsze rozwiązanie:
Rozważ ten prosty przykład, w jaki sposób ustawić limit czasu komunikacji () za pomocą metody select.select () (obecnie dostępnej prawie wszędzie w * nix). Można to również napisać za pomocą epoll / poll / kqueue, ale wariant select.select () może być dla Ciebie dobrym przykładem. Główne ograniczenia select.select () (szybkość i maks. 1024 FDS) nie mają zastosowania do twojego zadania.
Działa to pod * nix, nie tworzy wątków, nie używa sygnałów, może być wyrywane z dowolnego wątku (nie tylko głównego) i wystarczająco szybkie, aby odczytać 250 Mb / s danych ze standardowego wyjścia na moim komputerze (i5 2,3 GHz).
Wystąpił problem z dołączeniem stdout / stderr na końcu komunikacji. Jeśli masz duży program wyjściowy, może to prowadzić do dużego zużycia pamięci. Ale możesz wywołać komunikację () kilka razy z mniejszymi limitami czasu.
źródło
Możesz to zrobić za pomocą
select
źródło
Z powodzeniem korzystałem z procesu killableprocess w systemach Windows, Linux i Mac. Jeśli używasz Cygwin Python, będziesz potrzebować wersji killableprocess OSAF, ponieważ w przeciwnym razie natywne procesy systemu Windows nie zostaną zabite.
źródło
Chociaż nie przyglądałem się temu dokładnie, ten dekorator, który znalazłem w ActiveState, wydaje się być całkiem przydatny do tego rodzaju rzeczy. Wraz z
subprocess.Popen(..., close_fds=True)
przynajmniej jestem gotowy do skryptowania powłoki w Pythonie.źródło
To rozwiązanie zabija drzewo procesów w przypadku powłoki = True, przekazuje parametry do procesu (lub nie), ma limit czasu i pobiera wyjście standardowe, standardowe i wyjściowe wywołania zwrotnego (używa psutil dla drzewa_zabicia). Opierało się to na kilku rozwiązaniach opublikowanych w SO, w tym na jcollado. Publikowanie w odpowiedzi na komentarze Ansona i jradice w odpowiedzi jcollado. Testowane w Windows Srvr 2012 i Ubuntu 14.04. Pamiętaj, że w Ubuntu musisz zmienić wywołanie parent.children (...) na parent.get_children (...).
źródło
Istnieje pomysł, aby podklasować klasę Popen i rozszerzyć ją o kilka prostych dekoratorów metod. Nazwijmy to ExpiablePopen.
źródło
Miałem problem polegający na tym, że chciałem zakończyć podproces wielowątkowy, jeśli trwa to dłużej niż określony limit czasu. Chciałem ustawić limit czasu
Popen()
, ale to nie zadziałało. Potem zdałem sobie sprawę, żePopen().wait()
to jest równecall()
i dlatego wpadłem na pomysł, aby ustawić limit czasu w.wait(timeout=xxx)
metodzie, która w końcu zadziałała. W ten sposób rozwiązałem to w ten sposób:źródło
Niestety obowiązują mnie bardzo surowe zasady dotyczące ujawniania kodu źródłowego przez mojego pracodawcę, więc nie mogę podać rzeczywistego kodu. Ale moim zdaniem najlepszym rozwiązaniem jest utworzenie podklasy zastępującej
Popen.wait()
ankietę zamiast czekać w nieskończoność iPopen.__init__
zaakceptowanie parametru limitu czasu. Po wykonaniu tej czynności wszystkie pozostałePopen
metody (które wywołaniewait
) będą działać zgodnie z oczekiwaniami, w tymcommunicate
.źródło
https://pypi.python.org/pypi/python-subprocess2 zapewnia rozszerzenia modułu podprocesu, które pozwalają poczekać do pewnego okresu czasu, w przeciwnym razie zakończyć.
Więc poczekaj 10 sekund na zakończenie procesu, w przeciwnym razie zabij:
Jest to zgodne zarówno z systemem Windows, jak i Unix. „results” jest słownikiem, zawiera „returnCode”, który jest zwrotem aplikacji (lub None, jeśli trzeba ją zabić), a także „actionTaken”. który będzie „SUBPROCESS2_PROCESS_COMPLETED”, jeśli proces zakończy się normalnie, lub maska „SUBPROCESS2_PROCESS_TERMINATED” i SUBPROCESS2_PROCESS_KILLED w zależności od podjętych działań (szczegółowe informacje znajdują się w dokumentacji)
źródło
dla Pythona 2.6+ użyj gevent
źródło
python 2.7
źródło
źródło
Chciałem tylko napisać coś prostszego.
źródło