Я создал минимальный воспроизводимый пример, который можно клонировать и легко запустить на Github для тестирования: https://github.com/VirxEC/python-no-run
Если вы не хотите переходить на GitHub, фрагменты кода будут внизу этого поста.
При запуске Python из Rust в Windows (Linux работает нормально) чтение из стандартного ввода не позволяет подпроцессу Python запускать больше подпроцессов Python. Добавление быстрого sleep(1) позволяет запустить подпроцесс, и он работает нормально, но если для запуска подпроцесса требуется более 1 секунды, мне не повезло. Если это занимает менее 1 секунды, значит, я жду дольше, чем должен. Итак, использование sleep — это быстрый патч, который я реализовал на данный момент, но мне нужно правильное решение.
Эта проблема возникает только тогда, когда Python запускается из Rust только в Windows, и если вы запускаете Python в обычном режиме, все работает как положено. Однако мое приложение не является приложением Python, это графический интерфейс Rust, использующий Tauri с подкомпонентом Python, потому что есть большая библиотека, предназначенная только для Python и еще не портированная на Rust... и, возможно, никогда не будет портирована из-за к своим большим размерам.
Это также означает, что я не могу принимать другие несвязанные команды из основного процесса Rust, пока он порождает подпроцессы, потому что он просто не читает стандартный ввод и не ищет их. В моей реальной программе я запускаю новый «поток» из потоковой обработки, поэтому теоретически стандартный ввод может быть прочитан, в то время как подпроцессы запускаются на стороне. Теперь я не должен позволять чтению stdin во время порождения этих процессов, что просто добавляет дополнительную сложность, которая не нужна.
Проблема в том, что «Hello World» не рассылается спамом, потому что подпроцесс запускается, но не начинает выполняться. Я был в этом, и я в недоумении, что происходит. Я знаю, что это связано с stdin, но моя программа использует stdin и stdout для связи между основным процессом Rust и процессом Python.
Каким-то образом чтение со стандартного ввода в основном потоке блокирует запуск подпроцесса полностью. Я знаю это, потому что если вы создадите подпроцесс с помощью shell=True, то после выхода из цикла «Hello World» будет рассылаться спамом.
Также, если я добавлю sleep(1) сразу после создания подпроцесса, то все работает! Ура! Вот только это далеко от идеала. Это как пластырь, который недостаточно велик для раны, но, по крайней мере, частично ее закрывает. Я временно внедрил это решение в свою программу (в частности, sleep(3) для каждого порожденного подпроцесса), но возможно, что потребуется порождать многие десятки процессов, что в конечном итоге займет слишком много времени. Кроме того, для запуска процесса все еще может потребоваться больше 3 секунд, и мне бы не повезло, если бы это произошло.
Я удалил операторы импорта и т. д., потому что они не должны иметь большого значения. Их все еще можно найти в репозитории GitHub, где находится весь минимальный воспроизводимый пример, если вам интересно.
main.rs// Setup command
let mut command = Command::new("python");
command.args(["-u", "-c", "from main import start; start()"]);
command.current_dir(std::env::current_dir()?.join("python"));
// Pipe stdin so we can issue commands to tell Python what we want it to do
command.stdin(Stdio::piped());
// Spawn the process & take ownership of stdin
let mut child = command.spawn()?;
let mut stdin = child.stdin.take().unwrap();
// Give a second to make 100% sure Python has started
sleep(Duration::from_secs(1));
// Issue start command to Python, wait 5 seconds then tell it to stop
println!("Writing start");
stdin.write_all(b"start | \n")?;
sleep(Duration::from_secs(5));
println!("Writing stop");
stdin.write_all(b"stop | \n")?;
// Make sure the process has exited before we exit
child.wait()?;
main.pydef start():
procs = []
while True:
print("Listening for new command...")
command = sys.stdin.readline()
params = command.split(" | ")
print(f"Got command: {command}")
if params[0] == "stop":
print("Stopping...")
# Break from loop
break
elif params[0] == "start":
print("Starting...")
# Spawn the subprocess
proc = subprocess.Popen([sys.executable, "say-hi.py"])
# Add the subprocess to the list so we can kill it later
procs.append(proc)
print("Shutting down")
# Terminate processes
for proc in procs:
proc.terminate()
say-hi.pywhile True:
print(f"Hello world!", flush=True)
sleep(1)
Это похоже на инструкции, которые можно найти в README.md в репозитории GitHub.
cargo r и убедитесь, что ничего не происходитpython/main.py замените Popen([sys.executable, "say-hi.py"]) на Popen([sys.executable, "say-hi.py"], shell=True)cargo r и увидите, что «Hello world» рассылается спамом, когда выдается команда остановки, и никогда не останавливается.pythonshell может быть True или False, не важноpython -c "from main import start; start()", чтобы начать процессstart | точно и увидите, что "Hello world" рассылается спамомstop | точно и увидите, что сообщение «Hello world» перестало рассылаться спамом, все успешно обрабатываются.@AhmedAEK Попробуйте запустить его сами - есть причина, по которой я помещаю туда другие отпечатки, такие как print("Starting..."). Эти отпечатки отображаются просто отлично, потому что команды без вопросов принимаются через стандартный ввод. Просто "Hello World" не отображается.
похоже, что python неправильно подключал детей к стандартному выводу, то есть: proc = subprocess.Popen([sys.executable, "say-hi.py"],stdin=subprocess.PIPE,stdout=sys.stdout,stderr=sys.stderr) будет работать.
если честно, stdout и stderr должны быть подключены к каналу, и родитель должен быть тем, кто будет их печатать, а не направлять их непосредственно в стандартный вывод родителя ... представьте себе, что несколько программ печатают в нескольких потоках, это очень быстро становится грязным, родитель должен всегда синхронизировать печать со своим собственным стандартным выводом и не позволять дочерним элементам перенимать его.
Нет, процессы не запускаются. Если вы выполните shell=True и запустите программу, спам Hello World начнется после завершения программы и не остановится (не знаю, почему это так, но я сказал это в посте)
правильное подключение stdout и stderr устранило проблему на моей машине в Windows как с shell = True, так и без него ... вы пробовали? примечание: запуск python3.9 в Windows 10
Хорошо, тогда я попробую. Я выхожу с работы через несколько часов. Не уверен, почему это исправит это, но я не уверен, почему эта ошибка происходит так: p
Если вы хотите дать ответ, чтобы я мог его принять, тогда вперед.






subprocess.Popen с параметрами по умолчанию запрашивает дескриптор stdout и stdin из операционной системы в Windows ... который по какой-то причине зависает или терпит неудачу.
в любом случае передача родительского stdout или PIPE, похоже, исправляет это в Windows, просто заменяя
proc = subprocess.Popen([sys.executable, "say-hi.py"])
с
proc = subprocess.Popen([sys.executable, "say-hi.py"], stdin=subprocess.PIPE, stdout=sys.stdout, stderr=sys.stderr)
который просто передает идентификаторы родительского файла stdout дочернему для использования.
Спасибо за это! Теперь это имеет больше смысла, когда я знаю, что stdin=subprocess.PIPE было единственным, что мне было нужно... оно работает без stdout=sys.stdout и stderr=sys.stderr. Супер большое Вам спасибо!!!
Вы уверены, что ржавчина очищает стандартный ввод процесса, а не буферизует его?