# vim: fileencoding=utf-8 # from .base_format import Format from calculate.utils.files import Process from calculate.templates.format.base_format import FormatError from ..template_engine import ParametersContainer, Variables from ...variables.datavars import NamespaceNode from ...variables.loader import Datavars from typing import Union from os import path class PatchFormat(Format): FORMAT = 'patch' EXECUTABLE = True def __init__(self, patch_text: str, template_path: str, parameters: ParametersContainer, datavars: Union[Datavars, NamespaceNode, Variables]): self._patch_text = patch_text self._cwd_path = '/' self._last_level = 0 # Измененные файлы. self.changed_files = dict() # Предупреждения. self._warnings: list = [] def execute_format(self, target_path, chroot_path='/'): '''Метод для запуска работы формата.''' 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