Как ждать после завершения процесса, пока файл не будет разблокирован в 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, пока не будет достигнут конец файла. Подождите, пока процесс завершится.

Другие вопросы по тегам