Как ждать после завершения процесса, пока файл не будет разблокирован в Windows с помощью Python?
Описание проблемы
У меня есть unittest, который вызывает внешнюю программу и проводит некоторое тестирование. После этого внешний процесс уничтожается, и тест пытается очистить файлы, созданные внешней программой. Однако, если я просто позвоню unlink()
сразу после kill()
Команда на Windows 10, я получаю:
PermissionError: [WinError 32] The process cannot access the file because it is being used by another process: 'my.log'
Если я time.sleep(4)
перед звонком unlink()
все работает как положено. 4
был выбран произвольно, другие времена также работают.
MCVE
Этот MCVE имеет два файла. server.py
который просто блокирует файл регистрации и test_client.py
который вызывает сервер, убивает его и, наконец, пытается удалить файл журнала.
test_client.py
import pathlib
import subprocess
import sys
import time
# Create test folder
server_path = pathlib.Path('.')
server_path.mkdir(exist_ok=True)
server_file = pathlib.Path('server.py').resolve()
# Start server in test folder
proc = subprocess.Popen([sys.executable, str(server_file)], cwd=server_path)
time.sleep(4)
# Kill server
proc.kill()
# Activate the following line to avoid the PermissionError: [WinError 32] ...
#time.sleep(4)
# Clean up
pathlib.Path('my.log').unlink()
server.py
import time
import logging
logging.basicConfig(
filename='my.log',
level=logging.DEBUG)
logging.info('I just started my work')
while True:
time.sleep(1)
Вопрос
- Есть ли способ дождаться окончания убийства?
- Бонус, если код не зависит от платформы
1 ответ
Правильный путь
# Kill server
proc.kill()
# Activate the following line to avoid the PermissionError: [WinError 32] ...
proc.communicate()
# Clean up
pathlib.Path('my.log').unlink()
Причина, по которой он ведет себя так, требует некоторой документации.
Как указано в официальной документации Python,
Popen.kill () Убивает ребенка. В ОС Posix функция отправляет SIGKILL ребенку. В Windows kill() является псевдонимом terminate ().
Popen.terminate () Остановить ребенка. В операционных системах Posix метод отправляет SIGTERM ребенку. В Windows Win32 API-функция TerminateProcess() вызывается, чтобы остановить дочерний процесс.
В Windows, как уже говорилось, он вызывает функцию TerminateProcess в Win32 API. Хотя четко сказано, что
TerminateProcess является асинхронным; он инициирует прекращение и немедленно возвращается. Если вам нужно убедиться, что процесс завершен, вызовите функцию WaitForSingleObject с дескриптором процесса.
Следовательно, kill()
это просто "Попроси остановиться". Еще не остановлено. Таким образом, вам нужен метод для "ожидания" завершения процесса.
communicate()
предназначен для этой цели, как все еще указано в документе
Взаимодействовать с процессом: отправлять данные в stdin. Читайте данные из stdout и stderr, пока не будет достигнут конец файла. Подождите, пока процесс завершится.