Saya menggunakan modul subproses untuk memulai subproses dan terhubung ke aliran output (stdout). Saya ingin dapat menjalankan bacaan non-pemblokiran di stdout-nya. Apakah ada cara untuk membuat .readline tanpa pemblokiran atau untuk memeriksa apakah ada data di aliran sebelum saya memohon .readline
? Saya ingin ini portabel atau setidaknya berfungsi di Windows dan Linux.
di sini adalah bagaimana saya melakukannya untuk saat ini (Ini memblokir .readline
jika tidak ada data tersedia):
p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
python
io
subprocess
nonblocking
Mathieu Pagé
sumber
sumber
To avoid deadlocks: careful to: add \n to output, flush output, use readline() rather than read()
Jawaban:
fcntl
,select
,asyncproc
Tidak akan membantu dalam kasus ini.Cara tepercaya untuk membaca aliran tanpa memblokir apa pun sistem operasinya adalah dengan menggunakan
Queue.get_nowait()
:sumber
out.readline
memblokir utas, dan utas utama, dan saya harus menunggu sampai readline kembali sebelum semuanya berlanjut. Adakah cara mudah untuk mengatasi itu? (Saya membaca beberapa baris dari proses saya, yang juga merupakan file .py lain yang melakukan DB dan hal-hal lain)shelljob
pypi.python.org/pypi/shelljobSaya sering mengalami masalah yang sama; Program python yang saya tulis sering harus memiliki kemampuan untuk menjalankan beberapa fungsi utama sekaligus menerima input pengguna dari baris perintah (stdin). Cukup dengan menempatkan fungsi penanganan input pengguna di utas lain tidak menyelesaikan masalah karena
readline()
blok dan tidak memiliki batas waktu. Jika fungsi utama selesai dan tidak perlu lagi menunggu input pengguna lebih lanjut, saya biasanya ingin program saya keluar, tetapi tidak bisa karenareadline()
masih memblokir di utas lainnya menunggu garis. Solusi yang saya temukan untuk masalah ini adalah membuat stdin file non-blocking menggunakan modul fcntl:Menurut pendapat saya ini sedikit lebih bersih daripada menggunakan modul pilih atau sinyal untuk menyelesaikan masalah ini tetapi sekali lagi itu hanya bekerja pada UNIX ...
sumber
buffer_size
didefinisikan sebagai?Python 3.4 memperkenalkan API sementara baru untuk
asyncio
modul IO - asinkron .Pendekatannya mirip dengan
twisted
jawaban berbasis-oleh @Bryan Ward - mendefinisikan protokol dan metode-metodenya dipanggil segera setelah data siap:Lihat "Subproses" di dokumen .
Ada antarmuka tingkat tinggi
asyncio.create_subprocess_exec()
yang mengembalikanProcess
objek yang memungkinkan untuk membaca baris asynchroniosly menggunakanStreamReader.readline()
coroutine (dengan sintaksasync
/await
Python 3.5+ ):readline_and_kill()
melakukan tugas-tugas berikut:Setiap langkah dapat dibatasi oleh batas waktu detik jika perlu.
sumber
print(text, flush=True)
sehingga teks yang dicetak akan segera tersedia untuk pemantau panggilanreadline
. Ketika saya mengujinya dengan executable berbasis Fortran saya benar-benar ingin membungkus / menonton, itu tidak menyangga output itu, sehingga berperilaku seperti yang diharapkan.readline_and_kill
, dalam skrip kedua Anda, berfungsi sangat miripsubprocess.comunicate
dengan itu mengakhiri proses setelah satu operasi baca / tulis. Saya juga melihat bahwa Anda menggunakan satu pipastdout
, yang ditangani oleh subproses sebagai non-pemblokiran. Mencoba menggunakan keduanyastdout
danstderr
saya menemukan saya akhirnya memblokir .Coba modul asyncproc . Sebagai contoh:
Modul ini menangani semua threading seperti yang disarankan oleh S.Lott.
sumber
Anda dapat melakukannya dengan sangat mudah di Twisted . Tergantung pada basis kode Anda yang ada, ini mungkin tidak mudah digunakan, tetapi jika Anda sedang membangun aplikasi bengkok, maka hal-hal seperti ini menjadi hampir sepele. Anda membuat
ProcessProtocol
kelas, dan menggantioutReceived()
metode. Memutar (tergantung pada reaktor yang digunakan) biasanya hanyaselect()
loop besar dengan callback diinstal untuk menangani data dari deskriptor file yang berbeda (sering soket jaringan). JadioutReceived()
metode ini hanya menginstal panggilan balik untuk menangani data yang berasalSTDOUT
. Contoh sederhana yang menunjukkan perilaku ini adalah sebagai berikut:The twisted dokumentasi memiliki beberapa informasi yang baik ini.
Jika Anda membangun seluruh aplikasi Anda di sekitar Twisted, itu membuat komunikasi tidak sinkron dengan proses lain, lokal atau jauh, sangat elegan seperti ini. Di sisi lain, jika program Anda tidak dibangun di atas Twisted, ini tidak benar-benar bermanfaat. Semoga ini dapat bermanfaat bagi pembaca lain, bahkan jika itu tidak berlaku untuk aplikasi khusus Anda.
sumber
select
seharusnya tidak bekerja pada windows dengan deskriptor file, menurutdocsselect()
dia maksud adalah sama dengan Anda. Saya menganggap ini karenaTwisted
berfungsi pada windows ...asyncio
dari stdlib .select()
satu adalah yang paling portabel di unix dan suka-unix, tetapi ada juga dua reaktor yang tersedia untuk Windows: twistedmatrix.com/documents/current/core/howto/…Gunakan pilih & baca (1).
Untuk readline () - seperti:
sumber
select
seharusnya tidak bekerja di windows dengan deskriptor file, menurut docsproc.stdout.read()
sekecil apa pun argumennya. panggilan pemblokiran.OSError: [WinError 10093] Either the application has not called WSAStartup, or WSAStartup failed
Salah satu solusinya adalah membuat proses lain untuk melakukan pembacaan proses Anda, atau membuat utas proses dengan batas waktu.
Inilah versi utas dari fungsi batas waktu:
http://code.activestate.com/recipes/473878/
Namun, apakah Anda perlu membaca stdout saat datang? Solusi lain mungkin dengan membuang output ke file dan menunggu proses selesai menggunakan p.wait () .
sumber
Penafian: ini hanya berfungsi untuk tornado
Anda dapat melakukan ini dengan mengatur fd menjadi nonblocking dan kemudian menggunakan ioloop untuk mendaftarkan panggilan balik. Saya telah mengemas ini dalam telur yang disebut tornado_subprocess dan Anda dapat menginstalnya melalui PyPI:
sekarang kamu bisa melakukan sesuatu seperti ini:
Anda juga dapat menggunakannya dengan RequestHandler
sumber
threading.Thread
untuk membuat proses non-blocking baru? Saya menggunakannya dalamon_message
contoh websocket Tornado, dan itu berhasil dengan baik.select
, dengan deskriptor file, tidak )select
panggilan. Saya belum mencoba ini di Windows tetapi Anda mungkin akan mengalami masalah karena lib menggunakanfcntl
modul. Singkatnya: tidak, ini mungkin tidak akan berfungsi di Windows.Solusi yang ada tidak berhasil untuk saya (detail di bawah). Yang akhirnya berhasil adalah menerapkan readline menggunakan read (1) (berdasarkan jawaban ini ). Yang terakhir tidak memblokir:
Mengapa solusi yang ada tidak berfungsi:
sumber
q.get_nowait()
dari jawaban saya tidak boleh memblokir, pernah, itulah gunanya menggunakannya. 2. Thread yang mengeksekusi readline (enqueue_output()
fungsi ) keluar pada EOF misalnya, termasuk kasus ketika proses produksi-output dimatikan. Jika Anda percaya tidak demikian; tolong, berikan contoh kode minimal lengkap yang menunjukkan sebaliknya (mungkin sebagai pertanyaan baru ).dcmpid = myprocess
.Berikut adalah kode saya, digunakan untuk menangkap setiap output dari ASAP subproses, termasuk garis parsial. Ini memompa pada waktu yang sama dan stdout dan stderr dalam urutan yang hampir benar.
Diuji dan bekerja dengan benar di Python 2.7 linux & windows.
sumber
Saya menambahkan masalah ini untuk membaca beberapa subprocess.Popen stdout. Berikut adalah solusi baca yang tidak menghalangi saya:
sumber
msvcrt.kbhit()
sajaVersi non-blocking read ini tidak memerlukan modul khusus dan akan bekerja secara otomatis di sebagian besar distro Linux.
sumber
Berikut adalah solusi sederhana berdasarkan utas yang:
select
).stdout
danstderr
sinkron.asyncio
(yang mungkin bertentangan dengan perpustakaan lain).printer.py
reader.py
sumber
Menambahkan jawaban ini di sini karena memberikan kemampuan untuk mengatur pipa non-blocking pada Windows dan Unix.
Semua
ctypes
detailnya berkat jawaban @ techtonik .Ada versi yang sedikit dimodifikasi untuk digunakan pada sistem Unix dan Windows.
Dengan cara ini Anda dapat menggunakan fungsi dan pengecualian yang sama untuk kode Unix dan Windows.
Untuk menghindari membaca data yang tidak lengkap, saya akhirnya menulis generator readline saya sendiri (yang mengembalikan string byte untuk setiap baris).
Ini generator sehingga Anda dapat misalnya ...
sumber
readline()
tidak berfungsi dengan pipa non-blocking (seperti diatur menggunakanfcntl
) pada Python 2 - apakah menurut Anda itu tidak lagi benar? (jawaban saya berisi tautan (fcntl
) yang memberikan info yang sama tetapi sepertinya dihapus sekarang). (2) Lihat bagaimanamultiprocessing.connection.Pipe
menggunakanSetNamedPipeHandleState
Saya memiliki masalah dengan penanya asli, tetapi tidak ingin meminta utas. Saya mencampur solusi Jesse dengan membaca langsung () dari pipa, dan buffer-handler saya sendiri untuk membaca baris (namun, sub-proses saya - ping - selalu menulis baris penuh <ukuran halaman sistem). Saya menghindari kesibukan-menunggu dengan hanya membaca di io arloji terdaftar-gobject. Hari-hari ini saya biasanya menjalankan kode dalam gobject MainLoop untuk menghindari utas.
Pengamat itu
Dan program utama mengatur ping dan kemudian memanggil loop email gobject.
Pekerjaan lain dilampirkan ke callback di gobject.
sumber
Banyak hal yang jauh lebih baik dalam Python modern.
Berikut program anak sederhana, "hello.py":
Dan program untuk berinteraksi dengannya:
Itu mencetak:
Perhatikan bahwa pola aktual, yang juga oleh hampir semua jawaban sebelumnya, baik di sini maupun dalam pertanyaan terkait, adalah untuk mengatur deskriptor file stdout anak ke non-blocking dan kemudian polling dalam semacam loop pilih. Hari-hari ini, tentu saja, loop itu disediakan oleh asyncio.
sumber
The pilih modul membantu Anda menentukan di mana masukan yang berguna berikutnya adalah.
Namun, Anda hampir selalu lebih bahagia dengan utas terpisah. Satu tidak memblokir membaca stdin, yang lain tidak di mana pun Anda tidak ingin diblokir.
sumber
mengapa mengganggu utas & antrian? tidak seperti readline (), BufferedReader.read1 () tidak akan menunggu \ r \ n, ia mengembalikan ASAP jika ada output yang masuk.
sumber
read1
akan memblokir jika blok baca yang mendasari pertama, yang terjadi ketika pipa masih terbuka tetapi input tidak tersedia.Dalam kasus saya, saya membutuhkan modul logging yang menangkap output dari aplikasi latar belakang dan menambahnya (menambahkan perangko waktu, warna, dll.).
Saya berakhir dengan utas latar belakang yang melakukan I / O yang sebenarnya. Kode berikut hanya untuk platform POSIX. Saya menanggalkan bagian yang tidak penting.
Jika seseorang akan menggunakan binatang ini untuk jangka panjang, pertimbangkan untuk mengelola deskriptor terbuka. Dalam kasus saya itu bukan masalah besar.
sumber
Masalah saya agak berbeda karena saya ingin mengumpulkan stdout dan stderr dari proses yang berjalan, tetapi pada akhirnya sama karena saya ingin membuat output dalam widget seperti yang dihasilkan.
Saya tidak ingin menggunakan banyak solusi yang diusulkan menggunakan Antrian atau Utas tambahan karena mereka tidak perlu melakukan tugas umum seperti menjalankan skrip lain dan mengumpulkan hasilnya.
Setelah membaca solusi yang diusulkan dan dokumen python saya menyelesaikan masalah saya dengan implementasi di bawah ini. Ya itu hanya berfungsi untuk POSIX karena saya menggunakan
select
pemanggilan fungsi.Saya setuju bahwa dokumen membingungkan dan implementasinya canggung untuk tugas scripting yang umum. Saya percaya bahwa versi python yang lebih lama memiliki standar
Popen
dan penjelasan yang berbeda sehingga menciptakan banyak kebingungan. Ini tampaknya bekerja dengan baik untuk Python 2.7.12 dan 3.5.2.Kuncinya adalah mengatur
bufsize=1
buffer line dan kemudianuniversal_newlines=True
memproses sebagai file teks, bukan biner yang tampaknya menjadi default saat pengaturanbufsize=1
.ERROR, DEBUG dan VERBOSE hanyalah makro yang mencetak output ke terminal.
Solusi ini adalah IMHO 99,99% efektif karena masih menggunakan
readline
fungsi pemblokiran , jadi kami menganggap sub prosesnya bagus dan menampilkan garis yang lengkap.Saya menyambut umpan balik untuk meningkatkan solusi karena saya masih baru di Python.
sumber
Saya telah membuat perpustakaan berdasarkan solusi JF Sebastian . Anda bisa menggunakannya.
https://github.com/cenkalti/what
sumber
Bekerja dari jawaban JF Sebastian, dan beberapa sumber lain, saya telah mengumpulkan manajer subproses sederhana. Ini menyediakan permintaan pembacaan non-blocking, serta menjalankan beberapa proses secara paralel. Itu tidak menggunakan panggilan khusus OS (yang saya tahu) dan karenanya harus bekerja di mana saja.
Ini tersedia dari pypi, jadi adil
pip install shelljob
. Lihat halaman proyek untuk contoh dan dokumen lengkap.sumber
EDIT: Implementasi ini masih memblokir. Gunakan jawaban JFSebastian sebagai gantinya.
Saya mencoba jawaban teratas , tetapi risiko tambahan dan pemeliharaan kode utas mengkhawatirkan.Melihat melalui modul io (dan terbatas pada 2.6), saya menemukan BufferedReader. Ini adalah solusi tanpa-ulir saya yang tanpa ulir.sumber
for line in iter(p.stdout.readline, ""): # do stuff with the line
? Itu tanpa benang (utas tunggal) dan memblokir ketika kode Anda diblokir.Saya baru-baru ini menemukan pada masalah yang sama saya perlu membaca satu baris pada waktu dari aliran (ekor berjalan dalam proses) dalam mode non-blocking Saya ingin menghindari masalah berikutnya: tidak membakar cpu, jangan membaca aliran dengan satu byte ( seperti readline lakukan), dll
Berikut ini adalah implementasi saya https://gist.github.com/grubberr/5501e1a9760c3eab5e0a itu tidak mendukung windows (polling), tidak menangani EOF, tetapi berfungsi dengan baik untuk saya
sumber
timeout
seperti dalam solusi Anda) dan.readline()
membaca lebih dari satu byte pada suatu waktu (bufsize=1
berarti garis -buffered (hanya relevan untuk menulis)). Apa masalah lain yang Anda temukan? Jawaban hanya tautan tidak terlalu berguna.Ini adalah contoh untuk menjalankan perintah interaktif dalam subproses, dan stdout bersifat interaktif dengan menggunakan terminal pseudo. Anda dapat merujuk ke: https://stackoverflow.com/a/43012138/3555925
sumber
Solusi ini menggunakan
select
modul untuk "membaca data apa pun yang tersedia" dari aliran IO. Fungsi ini awalnya memblokir sampai data tersedia, tetapi kemudian hanya membaca data yang tersedia dan tidak memblokir lebih lanjut.Mengingat fakta bahwa ia menggunakan
select
modul, ini hanya berfungsi pada Unix.Kode ini sepenuhnya sesuai dengan PEP8.
sumber
Saya juga menghadapi masalah yang dijelaskan oleh Jesse dan menyelesaikannya dengan menggunakan "pilih" seperti yang dilakukan Bradley , Andy dan lainnya tetapi dalam mode pemblokiran untuk menghindari loop sibuk. Ia menggunakan dummy Pipe sebagai stdin palsu. Pilih blok dan tunggu stdin atau pipa siap. Ketika tombol ditekan stdin membuka blokir pilih dan nilai kunci dapat diambil dengan membaca (1). Ketika utas yang berbeda menulis ke pipa maka pipa membuka blokir pilih dan dapat diambil sebagai indikasi bahwa kebutuhan untuk stdin sudah berakhir. Berikut ini beberapa kode referensi:
sumber
Coba wexpect , yang merupakan alternatif windows dari pexpect .
sumber
Pada sistem seperti Unix dan Python 3.5+ ada
os.set_blocking
yang melakukan persis apa yang dikatakannya.Output ini:
Dengan
os.set_blocking
berkomentar itu:sumber
Berikut adalah modul yang mendukung pembacaan non-blocking dan penulisan latar belakang dengan python:
https://pypi.python.org/pypi/python-nonblock
Menyediakan fungsi,
nonblock_read yang akan membaca data dari stream, jika tersedia, jika tidak mengembalikan string kosong (atau Tidak ada jika stream ditutup di sisi lain dan semua data yang mungkin telah dibaca)
Anda juga dapat mempertimbangkan modul python-subprocess2,
https://pypi.python.org/pypi/python-subprocess2
yang menambah modul subproses. Jadi pada objek yang dikembalikan dari "subprocess.Popen" ditambahkan metode tambahan, jalankanInBackground. Ini memulai utas dan mengembalikan objek yang secara otomatis akan diisi ketika barang ditulis ke stdout / stderr, tanpa memblokir utas utama Anda.
Nikmati!
sumber