# vim: fileencoding=utf-8 # from .base_format import Format, FormatError from calculate.utils.files import Process from typing import NoReturn from os import path class PatchFormat(Format): FORMAT = 'patch' EXECUTABLE = True def __init__(self, patch_text: str, template_path: str, **kwargs): self._patch_text = patch_text self._cwd_path = '/' self._last_level = 0 self._init_command() # Измененные файлы. self.changed_files = dict() # Предупреждения. self._warnings: list = [] def _init_command(self) -> NoReturn: self._patch_cmd = "/usr/bin/patch" if not path.exists(self._patch_cmd): self._patch_cmd = None def execute_format(self, target_path: str, chroot_path: str = '/') -> dict: '''Метод для запуска работы формата.''' if self._patch_cmd is None: raise FormatError("the 'patch' format is not available") self._cwd_path = target_path if not path.isdir(self._cwd_path): # Если target_path -- путь к файлу, запускаем все процессы из # директории, в которой этот файл находится. self._cwd_path = path.dirname(self._cwd_path) if not path.exists(self._cwd_path): raise FormatError('root path does not exist', executable=True) if self._patch_text: self._patch_document() return self.changed_files else: raise FormatError('empty patch file', executable=True) def _patch_document(self): '''Метод, производящий наложение патча путем запуска процесса patch.''' # Сначала определяем на каком уровне накладываем патч. # Для этого запускаем утилиту patch с --dry-run и смотрим результат # выполнения. for level in range(0, 4): patch_dry_run = Process('patch', '--dry-run', '-p{}'.format(level), cwd=self._cwd_path) patch_dry_run.write(self._patch_text) if patch_dry_run.success(): break patch_dry_run = Process('patch', '-R', '--dry-run', '-p{}'.format(level), cwd=self._cwd_path) patch_dry_run.write(self._patch_text) if patch_dry_run.success(): return '' else: raise FormatError('correction failed', executable=True) self._last_level = level patch_run = Process('patch', '-p{}'.format(level), cwd=self._cwd_path) patch_run.write(self._patch_text) if patch_run.success(): for line in patch_run.read_lines(): if line.startswith('patching file'): changed_file_path = path.join(self._cwd_path, line[13:].strip()) if path.exists(changed_file_path): self.changed_files.update({changed_file_path: 'C'}) else: self.changed_files.update({changed_file_path: 'D'}) return patch_run.read() else: return '' def __bool__(self): return bool(self._patch_text) @property def warnings(self): return self._warnings